Initial commit: One Pipe System

完整的管理系统,包含账户管理、卡片管理、套餐管理、财务管理等功能模块。

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
sexygoat
2026-01-22 16:35:33 +08:00
commit 222e5bb11a
495 changed files with 145440 additions and 0 deletions

View File

@@ -0,0 +1,611 @@
<template>
<ArtTableFullScreen>
<div class="package-assign-page" id="table-full-screen">
<!-- 搜索栏 -->
<ArtSearchBar
v-model:filter="formFilters"
:items="formItems"
@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="handleSearch">查询</ElButton>
<ElButton type="success" @click="showAssignDialog">分配套餐</ElButton>
<ElButton @click="exportExcel">导出excel</ElButton>
</template>
</ArtTableHeader>
<!-- 表格 -->
<ArtTable
ref="tableRef"
row-key="id"
:loading="loading"
:data="tableData"
:currentPage="pagination.currentPage"
:pageSize="pagination.pageSize"
:total="pagination.total"
:marginTop="10"
@selection-change="handleSelectionChange"
@size-change="handleSizeChange"
@current-change="handleCurrentChange"
>
<template #default>
<ElTableColumn v-for="col in columns" :key="col.prop || col.type" v-bind="col" />
</template>
</ArtTable>
<!-- 分配套餐对话框 -->
<ElDialog
v-model="assignDialogVisible"
title="分配套餐"
width="600px"
align-center
:close-on-click-modal="false"
>
<ElForm
ref="assignFormRef"
:model="assignFormData"
:rules="assignRules"
label-width="120px"
>
<ElFormItem label="分销商" prop="distributor">
<ElSelect
v-model="assignFormData.distributor"
placeholder="请选择分销商"
style="width: 100%"
clearable
>
<ElOption label="北京优享科技有限公司" value="beijing_youxiang" />
<ElOption label="上海智联通信技术公司" value="shanghai_zhilian" />
<ElOption label="广州物联网络有限公司" value="guangzhou_wulian" />
<ElOption label="深圳云联科技股份公司" value="shenzhen_yunlian" />
<ElOption label="杭州通达网络服务公司" value="hangzhou_tongda" />
</ElSelect>
</ElFormItem>
<ElFormItem label="套餐系列" prop="packageSeries">
<ElSelect
v-model="assignFormData.packageSeries"
placeholder="请选择套餐系列"
style="width: 100%"
clearable
>
<ElOption label="畅玩系列" value="changwan" />
<ElOption label="如意系列" value="ruyi" />
<ElOption label="NB专享" value="nb_special" />
<ElOption label="大流量系列" value="big_data" />
<ElOption label="广电系列" value="gdtv" />
</ElSelect>
</ElFormItem>
<ElFormItem label="套餐名称" prop="packageName">
<ElSelect
v-model="assignFormData.packageName"
placeholder="请选择套餐名称"
style="width: 100%"
clearable
>
<ElOption label="随意联畅玩年卡套餐" value="changwan_yearly" />
<ElOption label="如意包年3G流量包" value="ruyi_3g" />
<ElOption label="Y-NB专享套餐" value="nb_special_package" />
<ElOption label="100G全国流量月卡套餐" value="big_data_100g" />
<ElOption label="广电飞悦卡无预存50G" value="gdtv_50g" />
</ElSelect>
</ElFormItem>
<ElFormItem label="分销金额" prop="distributionAmount">
<ElInputNumber
v-model="assignFormData.distributionAmount"
:min="0"
:max="9999"
:precision="2"
placeholder="请输入分销金额"
style="width: 100%"
/>
</ElFormItem>
<ElFormItem label="分销比例" prop="distributionRatio">
<ElInputNumber
v-model="assignFormData.distributionRatio"
:min="0"
:max="100"
:precision="1"
placeholder="请输入分销比例"
style="width: 100%"
/>
<span style="margin-left: 8px">%</span>
</ElFormItem>
<ElFormItem label="备注说明" prop="remark">
<ElInput
v-model="assignFormData.remark"
type="textarea"
placeholder="请输入备注说明(可选)"
:rows="3"
maxlength="200"
show-word-limit
/>
</ElFormItem>
</ElForm>
<template #footer>
<div class="dialog-footer">
<ElButton @click="assignDialogVisible = false">取消</ElButton>
<ElButton type="primary" @click="handleAssignSubmit" :loading="assignLoading">
确认分配
</ElButton>
</div>
</template>
</ElDialog>
</ElCard>
</div>
</ArtTableFullScreen>
</template>
<script setup lang="ts">
import { h } from 'vue'
import { ElTag, ElMessage, ElMessageBox, ElInputNumber } from 'element-plus'
import type { FormInstance, FormRules } from 'element-plus'
import { useCheckedColumns } from '@/composables/useCheckedColumns'
import ArtButtonTable from '@/components/core/forms/ArtButtonTable.vue'
import { SearchChangeParams, SearchFormItem } from '@/types'
defineOptions({ name: 'PackageAssign' })
const assignDialogVisible = ref(false)
const loading = ref(false)
const assignLoading = ref(false)
// 定义表单搜索初始值
const initialSearchState = {
distributor: '',
assignStatus: '',
packageName: '',
packageSeries: ''
}
// 响应式表单数据
const formFilters = reactive({ ...initialSearchState })
const pagination = reactive({
currentPage: 1,
pageSize: 20,
total: 0
})
// 表格数据
const tableData = ref<any[]>([])
// 表格实例引用
const tableRef = ref()
// 选中的行数据
const selectedRows = ref<any[]>([])
// 分配表单实例
const assignFormRef = ref<FormInstance>()
// 分配表单数据
const assignFormData = reactive({
distributor: '',
packageSeries: '',
packageName: '',
distributionAmount: 0,
distributionRatio: 0,
remark: ''
})
// 模拟数据
const mockData = [
{
id: 1,
packageSeries: '畅玩系列',
packageName: '随意联畅玩年卡套餐',
packageTraffic: 120,
packageType: '年套餐',
salesAmount: 168.0,
distributionAmount: 150.0,
distributionRatio: 10.7,
costPrice: 120.0,
assignStatus: '已分配',
distributorName: '北京优享科技有限公司'
},
{
id: 2,
packageSeries: '如意系列',
packageName: '如意包年3G流量包',
packageTraffic: 36,
packageType: '年套餐',
salesAmount: 98.0,
distributionAmount: 85.0,
distributionRatio: 13.3,
costPrice: 80.0,
assignStatus: '未分配',
distributorName: ''
},
{
id: 3,
packageSeries: 'NB专享',
packageName: 'Y-NB专享套餐',
packageTraffic: 24,
packageType: '月套餐',
salesAmount: 25.0,
distributionAmount: 22.0,
distributionRatio: 12.0,
costPrice: 20.0,
assignStatus: '已分配',
distributorName: '广州物联网络有限公司'
},
{
id: 4,
packageSeries: '大流量系列',
packageName: '100G全国流量月卡套餐',
packageTraffic: 100,
packageType: '月套餐',
salesAmount: 59.0,
distributionAmount: 50.0,
distributionRatio: 15.3,
costPrice: 45.0,
assignStatus: '已分配',
distributorName: '深圳云联科技股份公司'
},
{
id: 5,
packageSeries: '广电系列',
packageName: '广电飞悦卡无预存50G',
packageTraffic: 50,
packageType: '月套餐',
salesAmount: 39.0,
distributionAmount: 33.0,
distributionRatio: 15.4,
costPrice: 30.0,
assignStatus: '未分配',
distributorName: ''
}
]
// 重置表单
const handleReset = () => {
Object.assign(formFilters, { ...initialSearchState })
pagination.currentPage = 1
getPackageAssignList()
}
// 搜索处理
const handleSearch = () => {
console.log('搜索参数:', formFilters)
pagination.currentPage = 1
getPackageAssignList()
}
// 表单项变更处理
const handleFormChange = (params: SearchChangeParams): void => {
console.log('表单项变更:', params)
}
// 表单配置项
const formItems: SearchFormItem[] = [
{
label: '分销商',
prop: 'distributor',
type: 'select',
config: {
clearable: true,
placeholder: '全部'
},
options: () => [
{ label: '北京优享科技有限公司', value: 'beijing_youxiang' },
{ label: '上海智联通信技术公司', value: 'shanghai_zhilian' },
{ label: '广州物联网络有限公司', value: 'guangzhou_wulian' },
{ label: '深圳云联科技股份公司', value: 'shenzhen_yunlian' },
{ label: '杭州通达网络服务公司', value: 'hangzhou_tongda' }
],
onChange: handleFormChange
},
{
label: '分配状态',
prop: 'assignStatus',
type: 'select',
config: {
clearable: true,
placeholder: '全部'
},
options: () => [
{ label: '已分配', value: 'assigned' },
{ label: '未分配', value: 'unassigned' },
{ label: '分配中', value: 'assigning' }
],
onChange: handleFormChange
},
{
label: '套餐名称',
prop: 'packageName',
type: 'input',
config: {
clearable: true,
placeholder: '请输入套餐名称'
},
onChange: handleFormChange
},
{
label: '套餐系列',
prop: 'packageSeries',
type: 'select',
config: {
clearable: true,
placeholder: '全部'
},
options: () => [
{ label: '畅玩系列', value: 'changwan' },
{ label: '如意系列', value: 'ruyi' },
{ label: 'NB专享', value: 'nb_special' },
{ label: '大流量系列', value: 'big_data' },
{ label: '广电系列', value: 'gdtv' }
],
onChange: handleFormChange
}
]
// 列配置
const columnOptions = [
{ label: '勾选', type: 'selection' },
{ label: '套餐系列', prop: 'packageSeries' },
{ label: '套餐名称', prop: 'packageName' },
{ label: '套餐流量(GB)', prop: 'packageTraffic' },
{ label: '套餐类型', prop: 'packageType' },
{ label: '套餐销售金额', prop: 'salesAmount' },
{ label: '套餐分销金额', prop: 'distributionAmount' },
{ label: '分销比例', prop: 'distributionRatio' },
{ label: '成本价', prop: 'costPrice' },
{ label: '分配状态', prop: 'assignStatus' },
{ label: '操作', prop: 'operation' }
]
// 获取分配状态标签类型
const getAssignStatusType = (status: string) => {
switch (status) {
case '已分配':
return 'success'
case '未分配':
return 'warning'
case '分配中':
return 'primary'
default:
return 'info'
}
}
// 导出Excel
const exportExcel = () => {
if (selectedRows.value.length === 0) {
ElMessage.warning('请先选择要导出的数据')
return
}
ElMessage.success(`导出 ${selectedRows.value.length} 条套餐分配记录`)
}
// 显示分配对话框
const showAssignDialog = () => {
assignDialogVisible.value = true
// 重置表单
if (assignFormRef.value) {
assignFormRef.value.resetFields()
}
assignFormData.distributor = ''
assignFormData.packageSeries = ''
assignFormData.packageName = ''
assignFormData.distributionAmount = 0
assignFormData.distributionRatio = 0
assignFormData.remark = ''
}
// 编辑分配
const editAssign = (row: any) => {
ElMessage.info(`编辑分配: ${row.packageName}`)
}
// 取消分配
const cancelAssign = (row: any) => {
ElMessageBox.confirm(`确定要取消分配套餐"${row.packageName}"吗?`, '取消分配确认', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
})
.then(() => {
ElMessage.success('取消分配成功')
getPackageAssignList()
})
.catch(() => {
ElMessage.info('已取消操作')
})
}
// 动态列配置
const { columnChecks, columns } = useCheckedColumns(() => [
{ type: 'selection' },
{
prop: 'packageSeries',
label: '套餐系列',
width: 120
},
{
prop: 'packageName',
label: '套餐名称',
minWidth: 180
},
{
prop: 'packageTraffic',
label: '套餐流量(GB)',
width: 120,
formatter: (row) => `${row.packageTraffic}GB`
},
{
prop: 'packageType',
label: '套餐类型',
width: 100
},
{
prop: 'salesAmount',
label: '套餐销售金额',
width: 120,
formatter: (row) => `¥${row.salesAmount.toFixed(2)}`
},
{
prop: 'distributionAmount',
label: '套餐分销金额',
width: 120,
formatter: (row) => (row.distributionAmount ? `¥${row.distributionAmount.toFixed(2)}` : '-')
},
{
prop: 'distributionRatio',
label: '分销比例',
width: 100,
formatter: (row) => (row.distributionRatio ? `${row.distributionRatio}%` : '-')
},
{
prop: 'costPrice',
label: '成本价',
width: 100,
formatter: (row) => `¥${row.costPrice.toFixed(2)}`
},
{
prop: 'assignStatus',
label: '分配状态',
width: 100,
formatter: (row) => {
return h(ElTag, { type: getAssignStatusType(row.assignStatus) }, () => row.assignStatus)
}
},
{
prop: 'operation',
label: '操作',
width: 180,
formatter: (row: any) => {
const buttons = []
if (row.assignStatus === '已分配') {
buttons.push(
h(ArtButtonTable, {
text: '编辑',
onClick: () => editAssign(row)
}),
h(ArtButtonTable, {
text: '取消分配',
onClick: () => cancelAssign(row)
})
)
} else {
buttons.push(
h(ArtButtonTable, {
text: '分配',
onClick: () => showAssignDialog()
})
)
}
return h('div', { class: 'operation-buttons' }, buttons)
}
}
])
onMounted(() => {
getPackageAssignList()
})
// 获取套餐分配列表
const getPackageAssignList = async () => {
loading.value = true
try {
// 模拟API调用
await new Promise((resolve) => setTimeout(resolve, 500))
const startIndex = (pagination.currentPage - 1) * pagination.pageSize
const endIndex = startIndex + pagination.pageSize
const paginatedData = mockData.slice(startIndex, endIndex)
tableData.value = paginatedData
pagination.total = mockData.length
loading.value = false
} catch (error) {
console.error('获取套餐分配列表失败:', error)
loading.value = false
}
}
const handleRefresh = () => {
getPackageAssignList()
}
// 处理表格行选择变化
const handleSelectionChange = (selection: any[]) => {
selectedRows.value = selection
}
// 处理表格分页变化
const handleSizeChange = (newPageSize: number) => {
pagination.pageSize = newPageSize
getPackageAssignList()
}
const handleCurrentChange = (newCurrentPage: number) => {
pagination.currentPage = newCurrentPage
getPackageAssignList()
}
// 分配表单验证规则
const assignRules = reactive<FormRules>({
distributor: [{ required: true, message: '请选择分销商', trigger: 'change' }],
packageSeries: [{ required: true, message: '请选择套餐系列', trigger: 'change' }],
packageName: [{ required: true, message: '请选择套餐名称', trigger: 'change' }],
distributionAmount: [{ required: true, message: '请输入分销金额', trigger: 'blur' }],
distributionRatio: [{ required: true, message: '请输入分销比例', trigger: 'blur' }]
})
// 提交分配
const handleAssignSubmit = async () => {
if (!assignFormRef.value) return
await assignFormRef.value.validate((valid) => {
if (valid) {
assignLoading.value = true
// 模拟分配过程
setTimeout(() => {
ElMessage.success(
`套餐分配成功!分销商:${assignFormData.distributor},套餐:${assignFormData.packageName}`
)
assignDialogVisible.value = false
assignLoading.value = false
getPackageAssignList()
}, 2000)
}
})
}
</script>
<style lang="scss" scoped>
.package-assign-page {
// 可以添加特定样式
}
:deep(.operation-buttons) {
display: flex;
gap: 8px;
}
.dialog-footer {
text-align: right;
}
:deep(.el-form-item) {
margin-bottom: 20px;
}
</style>

View File

@@ -0,0 +1,523 @@
<template>
<ArtTableFullScreen>
<div class="package-batch-page" id="table-full-screen">
<!-- 搜索栏 -->
<ArtSearchBar
v-model:filter="formFilters"
:items="formItems"
@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="handleSearch">查询</ElButton>
<ElButton type="success" @click="showImportDialog">导入</ElButton>
</template>
</ArtTableHeader>
<!-- 表格 -->
<ArtTable
ref="tableRef"
row-key="id"
:loading="loading"
:data="tableData"
:currentPage="pagination.currentPage"
:pageSize="pagination.pageSize"
:total="pagination.total"
:marginTop="10"
@selection-change="handleSelectionChange"
@size-change="handleSizeChange"
@current-change="handleCurrentChange"
>
<template #default>
<ElTableColumn v-for="col in columns" :key="col.prop || col.type" v-bind="col" />
</template>
</ArtTable>
<!-- 导入对话框 -->
<ElDialog
v-model="importDialogVisible"
title="批量导入套餐"
width="500px"
align-center
:close-on-click-modal="false"
>
<!-- 顶部下载模板按钮 -->
<div class="template-section">
<ElButton type="primary" @click="downloadTemplate" icon="Download"> 下载模板 </ElButton>
<span class="template-tip">请先下载模板按模板格式填写后上传</span>
</div>
<ElDivider />
<ElForm
ref="importFormRef"
:model="importFormData"
:rules="importRules"
label-width="120px"
>
<ElFormItem label="导入类型" prop="importType">
<ElSelect
v-model="importFormData.importType"
placeholder="请选择导入类型"
style="width: 100%"
clearable
>
<ElOption label="套餐批量导入" value="package_batch" />
<ElOption label="套餐更新导入" value="package_update" />
<ElOption label="套餐停用导入" value="package_disable" />
<ElOption label="套餐启用导入" value="package_enable" />
</ElSelect>
</ElFormItem>
<ElFormItem label="上传Excel文件" prop="excelFile">
<ElUpload
ref="uploadRef"
:limit="1"
:on-exceed="handleExceed"
:before-upload="beforeUpload"
:on-change="handleFileChange"
:auto-upload="false"
accept=".xlsx,.xls"
drag
>
<ElIcon class="el-icon--upload"><UploadFilled /></ElIcon>
<div class="el-upload__text"> 将文件拖到此处<em>点击上传</em> </div>
<template #tip>
<div class="el-upload__tip"> 只能上传 xlsx/xls 文件且不超过 10MB </div>
</template>
</ElUpload>
</ElFormItem>
<ElFormItem label="备注说明" prop="remark">
<ElInput
v-model="importFormData.remark"
type="textarea"
placeholder="请输入备注说明(可选)"
:rows="3"
maxlength="200"
show-word-limit
/>
</ElFormItem>
</ElForm>
<template #footer>
<div class="dialog-footer">
<ElButton @click="importDialogVisible = false">取消</ElButton>
<ElButton type="primary" @click="handleImportSubmit" :loading="importLoading">
确认导入
</ElButton>
</div>
</template>
</ElDialog>
</ElCard>
</div>
</ArtTableFullScreen>
</template>
<script setup lang="ts">
import { h } from 'vue'
import { ElTag, ElMessage, ElUpload, ElIcon, ElDivider } from 'element-plus'
import { UploadFilled, Download } from '@element-plus/icons-vue'
import type { FormInstance, UploadInstance, UploadRawFile, UploadFile } from 'element-plus'
import type { FormRules } from 'element-plus'
import { useCheckedColumns } from '@/composables/useCheckedColumns'
import ArtButtonTable from '@/components/core/forms/ArtButtonTable.vue'
import { SearchChangeParams, SearchFormItem } from '@/types'
defineOptions({ name: 'PackageBatch' })
const importDialogVisible = ref(false)
const loading = ref(false)
const importLoading = ref(false)
// 定义表单搜索初始值
const initialSearchState = {
importType: ''
}
// 响应式表单数据
const formFilters = reactive({ ...initialSearchState })
const pagination = reactive({
currentPage: 1,
pageSize: 20,
total: 0
})
// 表格数据
const tableData = ref<any[]>([])
// 表格实例引用
const tableRef = ref()
// 选中的行数据
const selectedRows = ref<any[]>([])
// 导入表单实例
const importFormRef = ref<FormInstance>()
const uploadRef = ref<UploadInstance>()
// 导入表单数据
const importFormData = reactive({
importType: '',
excelFile: null as File | null,
remark: ''
})
// 模拟数据
const mockData = [
{
id: 1,
importTotal: 1000,
success: 980,
failed: 20,
importType: '套餐批量导入',
operator: '张若暄',
operationTime: '2025-11-08 10:30:00'
},
{
id: 2,
importTotal: 500,
success: 500,
failed: 0,
importType: '套餐更新导入',
operator: '孔丽娟',
operationTime: '2025-11-07 14:15:00'
},
{
id: 3,
importTotal: 300,
success: 285,
failed: 15,
importType: '套餐停用导入',
operator: '李佳音',
operationTime: '2025-11-06 09:45:00'
},
{
id: 4,
importTotal: 800,
success: 795,
failed: 5,
importType: '套餐启用导入',
operator: '赵强',
operationTime: '2025-11-05 16:20:00'
},
{
id: 5,
importTotal: 150,
success: 145,
failed: 5,
importType: '套餐批量导入',
operator: '张若暄',
operationTime: '2025-11-04 11:30:00'
}
]
// 重置表单
const handleReset = () => {
Object.assign(formFilters, { ...initialSearchState })
pagination.currentPage = 1
getBatchImportList()
}
// 搜索处理
const handleSearch = () => {
console.log('搜索参数:', formFilters)
pagination.currentPage = 1
getBatchImportList()
}
// 表单项变更处理
const handleFormChange = (params: SearchChangeParams): void => {
console.log('表单项变更:', params)
}
// 表单配置项
const formItems: SearchFormItem[] = [
{
label: '导入类型',
prop: 'importType',
type: 'select',
config: {
clearable: true,
placeholder: '全部'
},
options: () => [
{ label: '套餐批量导入', value: 'package_batch' },
{ label: '套餐更新导入', value: 'package_update' },
{ label: '套餐停用导入', value: 'package_disable' },
{ label: '套餐启用导入', value: 'package_enable' }
],
onChange: handleFormChange
}
]
// 列配置
const columnOptions = [
{ label: '勾选', type: 'selection' },
{ label: '导入总数', prop: 'importTotal' },
{ label: '成功', prop: 'success' },
{ label: '失败', prop: 'failed' },
{ label: '导入类型', prop: 'importType' },
{ label: '操作人', prop: 'operator' },
{ label: '操作时间', prop: 'operationTime' },
{ label: '操作', prop: 'operation' }
]
// 获取导入类型标签类型
const getImportTypeTagType = (type: string) => {
switch (type) {
case '套餐批量导入':
return 'primary'
case '套餐更新导入':
return 'success'
case '套餐停用导入':
return 'danger'
case '套餐启用导入':
return 'warning'
default:
return 'info'
}
}
// 显示导入对话框
const showImportDialog = () => {
importDialogVisible.value = true
// 重置表单
if (importFormRef.value) {
importFormRef.value.resetFields()
}
importFormData.importType = ''
importFormData.excelFile = null
importFormData.remark = ''
}
// 下载模板
const downloadTemplate = () => {
ElMessage.success('正在下载批量导入模板...')
// 这里可以实现实际的模板下载功能
}
// 查看详情
const viewDetails = (row: any) => {
ElMessage.info(`查看导入详情: ${row.importType} - 导入总数:${row.importTotal}`)
}
// 重新导入
const retryImport = (row: any) => {
ElMessage.info(`重新导入: ${row.importType}`)
}
// 删除记录
const deleteRecord = (row: any) => {
ElMessage.info(`删除导入记录: ${row.id}`)
}
// 动态列配置
const { columnChecks, columns } = useCheckedColumns(() => [
{ type: 'selection' },
{
prop: 'importTotal',
label: '导入总数',
formatter: (row) => `${row.importTotal} `
},
{
prop: 'success',
label: '成功',
formatter: (row) => {
return h(ElTag, { type: 'success' }, () => `${row.success}`)
}
},
{
prop: 'failed',
label: '失败',
formatter: (row) => {
return h(ElTag, { type: row.failed > 0 ? 'danger' : 'success' }, () => `${row.failed}`)
}
},
{
prop: 'importType',
label: '导入类型',
formatter: (row) => {
return h(ElTag, { type: getImportTypeTagType(row.importType) }, () => row.importType)
}
},
{
prop: 'operator',
label: '操作人'
},
{
prop: 'operationTime',
label: '操作时间'
},
{
prop: 'operation',
label: '操作',
width: 220,
formatter: (row: any) => {
return h('div', { class: 'operation-buttons' }, [
h(ArtButtonTable, {
text: '查看',
onClick: () => viewDetails(row)
}),
h(ArtButtonTable, {
text: '重试',
onClick: () => retryImport(row)
}),
h(ArtButtonTable, {
text: '删除',
onClick: () => deleteRecord(row)
})
])
}
}
])
onMounted(() => {
getBatchImportList()
})
// 获取批量导入列表
const getBatchImportList = async () => {
loading.value = true
try {
// 模拟API调用
await new Promise((resolve) => setTimeout(resolve, 500))
const startIndex = (pagination.currentPage - 1) * pagination.pageSize
const endIndex = startIndex + pagination.pageSize
const paginatedData = mockData.slice(startIndex, endIndex)
tableData.value = paginatedData
pagination.total = mockData.length
loading.value = false
} catch (error) {
console.error('获取批量导入列表失败:', error)
loading.value = false
}
}
const handleRefresh = () => {
getBatchImportList()
}
// 处理表格行选择变化
const handleSelectionChange = (selection: any[]) => {
selectedRows.value = selection
}
// 处理表格分页变化
const handleSizeChange = (newPageSize: number) => {
pagination.pageSize = newPageSize
getBatchImportList()
}
const handleCurrentChange = (newCurrentPage: number) => {
pagination.currentPage = newCurrentPage
getBatchImportList()
}
// 文件上传限制
const handleExceed = () => {
ElMessage.warning('最多只能上传一个文件')
}
// 文件上传前检查
const beforeUpload = (file: UploadRawFile) => {
const isExcel =
file.type === 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' ||
file.type === 'application/vnd.ms-excel'
const isLt10M = file.size / 1024 / 1024 < 10
if (!isExcel) {
ElMessage.error('只能上传 Excel 文件!')
return false
}
if (!isLt10M) {
ElMessage.error('上传文件大小不能超过 10MB!')
return false
}
return false // 阻止自动上传
}
// 文件变化处理
const handleFileChange = (file: UploadFile) => {
if (file.raw) {
importFormData.excelFile = file.raw
}
}
// 导入表单验证规则
const importRules = reactive<FormRules>({
importType: [{ required: true, message: '请选择导入类型', trigger: 'change' }],
excelFile: [{ required: true, message: '请上传Excel文件', trigger: 'change' }]
})
// 提交导入
const handleImportSubmit = async () => {
if (!importFormRef.value) return
// 检查文件是否上传
if (!importFormData.excelFile) {
ElMessage.error('请先上传Excel文件')
return
}
await importFormRef.value.validate((valid) => {
if (valid) {
importLoading.value = true
// 模拟导入过程
setTimeout(() => {
ElMessage.success(`${importFormData.importType}批量导入提交成功!`)
importDialogVisible.value = false
importLoading.value = false
getBatchImportList()
}, 2000)
}
})
}
</script>
<style lang="scss" scoped>
.package-batch-page {
// 可以添加特定样式
}
.template-section {
display: flex;
gap: 12px;
align-items: center;
margin-bottom: 16px;
.template-tip {
font-size: 12px;
color: #909399;
}
}
:deep(.operation-buttons) {
display: flex;
gap: 8px;
}
.dialog-footer {
text-align: right;
}
:deep(.el-upload-dragger) {
padding: 40px;
}
:deep(.el-form-item) {
margin-bottom: 20px;
}
</style>

View File

@@ -0,0 +1,585 @@
<template>
<ArtTableFullScreen>
<div class="package-change-page" id="table-full-screen">
<!-- 搜索栏 -->
<ArtSearchBar
v-model:filter="formFilters"
:items="formItems"
@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="handleSearch">查询</ElButton>
<ElButton @click="exportExcel">导出excel</ElButton>
</template>
</ArtTableHeader>
<!-- 表格 -->
<ArtTable
ref="tableRef"
row-key="id"
:loading="loading"
:data="tableData"
:currentPage="pagination.currentPage"
:pageSize="pagination.pageSize"
:total="pagination.total"
:marginTop="10"
@selection-change="handleSelectionChange"
@size-change="handleSizeChange"
@current-change="handleCurrentChange"
>
<template #default>
<ElTableColumn v-for="col in columns" :key="col.prop || col.type" v-bind="col" />
</template>
</ArtTable>
<!-- 套餐变更对话框 -->
<ElDialog
v-model="changeDialogVisible"
title="套餐变更"
width="600px"
align-center
:close-on-click-modal="false"
>
<ElForm
ref="changeFormRef"
:model="changeFormData"
:rules="changeRules"
label-width="120px"
>
<ElFormItem label="当前套餐" prop="currentPackage">
<ElInput
v-model="changeFormData.currentPackage"
placeholder="当前套餐"
readonly
style="width: 100%"
/>
</ElFormItem>
<ElFormItem label="变更套餐" prop="newPackage">
<ElSelect
v-model="changeFormData.newPackage"
placeholder="请选择要变更的套餐"
style="width: 100%"
clearable
>
<ElOption label="随意联畅玩年卡套餐" value="changwan_yearly" />
<ElOption label="如意包年3G流量包" value="ruyi_3g" />
<ElOption label="Y-NB专享套餐" value="nb_special" />
<ElOption label="100G全国流量月卡套餐" value="big_data_100g" />
<ElOption label="广电飞悦卡无预存50G" value="gdtv_50g" />
<ElOption label="联通大王卡" value="unicom_dawang" />
<ElOption label="移动花卡宝藏版" value="mobile_huaka" />
<ElOption label="电信星卡" value="telecom_star" />
</ElSelect>
</ElFormItem>
<ElFormItem label="变更原因" prop="changeReason">
<ElSelect
v-model="changeFormData.changeReason"
placeholder="请选择变更原因"
style="width: 100%"
clearable
>
<ElOption label="用户主动变更" value="user_request" />
<ElOption label="套餐到期升级" value="package_upgrade" />
<ElOption label="业务需求调整" value="business_adjustment" />
<ElOption label="系统自动变更" value="system_auto" />
<ElOption label="其他原因" value="other" />
</ElSelect>
</ElFormItem>
<ElFormItem label="备注说明" prop="remark">
<ElInput
v-model="changeFormData.remark"
type="textarea"
placeholder="请输入备注说明(可选)"
:rows="3"
maxlength="200"
show-word-limit
/>
</ElFormItem>
</ElForm>
<template #footer>
<div class="dialog-footer">
<ElButton @click="changeDialogVisible = false">取消</ElButton>
<ElButton type="primary" @click="handleChangeSubmit" :loading="changeLoading">
确认变更
</ElButton>
</div>
</template>
</ElDialog>
</ElCard>
</div>
</ArtTableFullScreen>
</template>
<script setup lang="ts">
import { h } from 'vue'
import { ElTag, ElMessage, ElMessageBox } from 'element-plus'
import type { FormInstance, FormRules } from 'element-plus'
import { useCheckedColumns } from '@/composables/useCheckedColumns'
import ArtButtonTable from '@/components/core/forms/ArtButtonTable.vue'
import { SearchChangeParams, SearchFormItem } from '@/types'
defineOptions({ name: 'PackageChange' })
const changeDialogVisible = ref(false)
const loading = ref(false)
const changeLoading = ref(false)
// 定义表单搜索初始值
const initialSearchState = {
isDistribution: '',
cardStatus: '',
cardType: '',
cardCompany: '',
distributor: '',
iccid: '',
accessNumber: '',
virtualNumber: ''
}
// 响应式表单数据
const formFilters = reactive({ ...initialSearchState })
const pagination = reactive({
currentPage: 1,
pageSize: 20,
total: 0
})
// 表格数据
const tableData = ref<any[]>([])
// 表格实例引用
const tableRef = ref()
// 选中的行数据
const selectedRows = ref<any[]>([])
// 套餐变更表单实例
const changeFormRef = ref<FormInstance>()
// 套餐变更表单数据
const changeFormData = reactive({
currentPackage: '',
newPackage: '',
changeReason: '',
remark: ''
})
// 选中的行数据
const selectedRowData = ref<any>({})
// 模拟数据
const mockData = [
{
id: 1,
iccid: '89860621370079892035',
accessNumber: '1440012345678',
virtualNumber: '100001',
cardCompany: '联通',
expiryDate: '2025-12-31',
packageName: '随意联畅玩年卡套餐',
cardType: '2G/3G/4G',
distributorName: '北京优享科技有限公司',
cardStatus: '正常'
},
{
id: 2,
iccid: '89860621370079892036',
accessNumber: '1440012345679',
virtualNumber: '100002',
cardCompany: '移动',
expiryDate: '2025-11-30',
packageName: '如意包年3G流量包',
cardType: 'NB-IoT',
distributorName: '上海智联通信技术公司',
cardStatus: '停机'
},
{
id: 3,
iccid: '89860621370079892037',
accessNumber: '1440012345680',
virtualNumber: '100003',
cardCompany: '电信',
expiryDate: '2026-01-15',
packageName: 'Y-NB专享套餐',
cardType: '4G/5G',
distributorName: '广州物联网络有限公司',
cardStatus: '正常'
},
{
id: 4,
iccid: '89860621370079892038',
accessNumber: '1440012345681',
virtualNumber: '100004',
cardCompany: '联通',
expiryDate: '2025-10-20',
packageName: '100G全国流量月卡套餐',
cardType: '2G/3G/4G',
distributorName: '深圳云联科技股份公司',
cardStatus: '欠费停机'
},
{
id: 5,
iccid: '89860621370079892039',
accessNumber: '1440012345682',
virtualNumber: '100005',
cardCompany: '广电',
expiryDate: '2025-09-10',
packageName: '广电飞悦卡无预存50G',
cardType: '4G/5G',
distributorName: '杭州通达网络服务公司',
cardStatus: '正常'
}
]
// 重置表单
const handleReset = () => {
Object.assign(formFilters, { ...initialSearchState })
pagination.currentPage = 1
getPackageChangeList()
}
// 搜索处理
const handleSearch = () => {
console.log('搜索参数:', formFilters)
pagination.currentPage = 1
getPackageChangeList()
}
// 表单项变更处理
const handleFormChange = (params: SearchChangeParams): void => {
console.log('表单项变更:', params)
}
// 表单配置项
const formItems: SearchFormItem[] = [
{
label: '是否分销',
prop: 'isDistribution',
type: 'select',
config: {
clearable: true,
placeholder: '全部'
},
options: () => [
{ label: '是', value: 'yes' },
{ label: '否', value: 'no' }
],
onChange: handleFormChange
},
{
label: '卡状态',
prop: 'cardStatus',
type: 'select',
config: {
clearable: true,
placeholder: '全部'
},
options: () => [
{ label: '正常', value: 'normal' },
{ label: '停机', value: 'suspended' },
{ label: '欠费停机', value: 'overdue_suspended' },
{ label: '已销户', value: 'closed' },
{ label: '测试期', value: 'testing' }
],
onChange: handleFormChange
},
{
label: '卡片类型',
prop: 'cardType',
type: 'select',
config: {
clearable: true,
placeholder: '全部'
},
options: () => [
{ label: '2G/3G/4G', value: '2g_3g_4g' },
{ label: '4G/5G', value: '4g_5g' },
{ label: 'NB-IoT', value: 'nb_iot' },
{ label: 'eMTC', value: 'emtc' }
],
onChange: handleFormChange
},
{
label: '开卡公司',
prop: 'cardCompany',
type: 'select',
config: {
clearable: true,
placeholder: '全部'
},
options: () => [
{ label: '联通', value: 'unicom' },
{ label: '移动', value: 'mobile' },
{ label: '电信', value: 'telecom' },
{ label: '广电', value: 'gdtv' }
],
onChange: handleFormChange
},
{
label: '分销商',
prop: 'distributor',
type: 'input',
config: {
clearable: true,
placeholder: '请输入分销商'
},
onChange: handleFormChange
},
{
label: 'ICCID号',
prop: 'iccid',
type: 'input',
config: {
clearable: true,
placeholder: '请输入ICCID号'
},
onChange: handleFormChange
},
{
label: '接入号',
prop: 'accessNumber',
type: 'input',
config: {
clearable: true,
placeholder: '请输入接入号'
},
onChange: handleFormChange
},
{
label: '虚拟号',
prop: 'virtualNumber',
type: 'input',
config: {
clearable: true,
placeholder: '请输入虚拟号'
},
onChange: handleFormChange
}
]
// 列配置
const columnOptions = [
{ label: '勾选', type: 'selection' },
{ label: 'ICCID号', prop: 'iccid' },
{ label: '接入号码', prop: 'accessNumber' },
{ label: '虚拟号', prop: 'virtualNumber' },
{ label: '开卡公司', prop: 'cardCompany' },
{ label: '到期时间', prop: 'expiryDate' },
{ label: '套餐', prop: 'packageName' },
{ label: '卡片类型', prop: 'cardType' },
{ label: '分销商姓名', prop: 'distributorName' },
{ label: '卡状态', prop: 'cardStatus' },
{ label: '操作', prop: 'operation' }
]
// 获取卡状态标签类型
const getCardStatusType = (status: string) => {
switch (status) {
case '正常':
return 'success'
case '停机':
return 'warning'
case '欠费停机':
return 'danger'
case '已销户':
return 'info'
case '测试期':
return 'primary'
default:
return 'info'
}
}
// 导出Excel
const exportExcel = () => {
if (selectedRows.value.length === 0) {
ElMessage.warning('请先选择要导出的数据')
return
}
ElMessage.success(`导出 ${selectedRows.value.length} 条套餐变更记录`)
}
// 显示套餐变更对话框
const showChangeDialog = (row: any) => {
changeDialogVisible.value = true
selectedRowData.value = row
// 重置表单
if (changeFormRef.value) {
changeFormRef.value.resetFields()
}
changeFormData.currentPackage = row.packageName
changeFormData.newPackage = ''
changeFormData.changeReason = ''
changeFormData.remark = ''
}
// 动态列配置
const { columnChecks, columns } = useCheckedColumns(() => [
{ type: 'selection' },
{
prop: 'iccid',
label: 'ICCID号',
minWidth: 180
},
{
prop: 'accessNumber',
label: '接入号码',
width: 140
},
{
prop: 'virtualNumber',
label: '虚拟号',
width: 100
},
{
prop: 'cardCompany',
label: '开卡公司',
width: 100
},
{
prop: 'expiryDate',
label: '到期时间',
width: 120
},
{
prop: 'packageName',
label: '套餐',
minWidth: 180
},
{
prop: 'cardType',
label: '卡片类型',
width: 120
},
{
prop: 'distributorName',
label: '分销商姓名',
minWidth: 180
},
{
prop: 'cardStatus',
label: '卡状态',
width: 100,
formatter: (row) => {
return h(ElTag, { type: getCardStatusType(row.cardStatus) }, () => row.cardStatus)
}
},
{
prop: 'operation',
label: '操作',
width: 120,
formatter: (row: any) => {
return h('div', { class: 'operation-buttons' }, [
h(ArtButtonTable, {
text: '套餐变更',
onClick: () => showChangeDialog(row)
})
])
}
}
])
onMounted(() => {
getPackageChangeList()
})
// 获取套餐变更列表
const getPackageChangeList = async () => {
loading.value = true
try {
// 模拟API调用
await new Promise((resolve) => setTimeout(resolve, 500))
const startIndex = (pagination.currentPage - 1) * pagination.pageSize
const endIndex = startIndex + pagination.pageSize
const paginatedData = mockData.slice(startIndex, endIndex)
tableData.value = paginatedData
pagination.total = mockData.length
loading.value = false
} catch (error) {
console.error('获取套餐变更列表失败:', error)
loading.value = false
}
}
const handleRefresh = () => {
getPackageChangeList()
}
// 处理表格行选择变化
const handleSelectionChange = (selection: any[]) => {
selectedRows.value = selection
}
// 处理表格分页变化
const handleSizeChange = (newPageSize: number) => {
pagination.pageSize = newPageSize
getPackageChangeList()
}
const handleCurrentChange = (newCurrentPage: number) => {
pagination.currentPage = newCurrentPage
getPackageChangeList()
}
// 套餐变更表单验证规则
const changeRules = reactive<FormRules>({
newPackage: [{ required: true, message: '请选择要变更的套餐', trigger: 'change' }],
changeReason: [{ required: true, message: '请选择变更原因', trigger: 'change' }]
})
// 提交套餐变更
const handleChangeSubmit = async () => {
if (!changeFormRef.value) return
await changeFormRef.value.validate((valid) => {
if (valid) {
changeLoading.value = true
// 模拟变更过程
setTimeout(() => {
ElMessage.success(
`套餐变更成功ICCID${selectedRowData.value.iccid},从${changeFormData.currentPackage}变更为${changeFormData.newPackage}`
)
changeDialogVisible.value = false
changeLoading.value = false
getPackageChangeList()
}, 2000)
}
})
}
</script>
<style lang="scss" scoped>
.package-change-page {
// 可以添加特定样式
}
:deep(.operation-buttons) {
display: flex;
gap: 8px;
}
.dialog-footer {
text-align: right;
}
:deep(.el-form-item) {
margin-bottom: 20px;
}
</style>

View File

@@ -0,0 +1,452 @@
<template>
<ArtTableFullScreen>
<div class="package-commission-page" id="table-full-screen">
<!-- 搜索栏 -->
<ArtSearchBar
v-model:filter="formFilters"
:items="formItems"
@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="handleSearch">搜索</ElButton>
<ElButton type="success" @click="showAddDialog">新增</ElButton>
<ElButton @click="exportExcel">导出excel</ElButton>
</template>
</ArtTableHeader>
<!-- 表格 -->
<ArtTable
ref="tableRef"
row-key="id"
:loading="loading"
:data="tableData"
:currentPage="pagination.currentPage"
:pageSize="pagination.pageSize"
:total="pagination.total"
:marginTop="10"
@selection-change="handleSelectionChange"
@size-change="handleSizeChange"
@current-change="handleCurrentChange"
>
<template #default>
<ElTableColumn v-for="col in columns" :key="col.prop || col.type" v-bind="col" />
</template>
</ArtTable>
<!-- 新增套餐佣金网卡对话框 -->
<ElDialog
v-model="addDialogVisible"
title="新增套餐佣金网卡"
width="500px"
align-center
:close-on-click-modal="false"
>
<ElForm ref="addFormRef" :model="addFormData" :rules="addRules" label-width="120px">
<ElFormItem label="卡号" prop="cardNumber">
<ElInput v-model="addFormData.cardNumber" placeholder="请输入卡号" clearable />
</ElFormItem>
<ElFormItem label="月份" prop="month">
<ElDatePicker
v-model="addFormData.month"
type="month"
placeholder="请选择月份"
style="width: 100%"
format="YYYY-MM"
value-format="YYYY-MM"
/>
</ElFormItem>
</ElForm>
<template #footer>
<div class="dialog-footer">
<ElButton @click="addDialogVisible = false">取消</ElButton>
<ElButton type="primary" @click="handleAddSubmit" :loading="addLoading">
确认新增
</ElButton>
</div>
</template>
</ElDialog>
</ElCard>
</div>
</ArtTableFullScreen>
</template>
<script setup lang="ts">
import { h } from 'vue'
import { ElTag, ElMessage, ElMessageBox, ElDatePicker } from 'element-plus'
import type { FormInstance, FormRules } from 'element-plus'
import { useCheckedColumns } from '@/composables/useCheckedColumns'
import ArtButtonTable from '@/components/core/forms/ArtButtonTable.vue'
import { SearchChangeParams, SearchFormItem } from '@/types'
defineOptions({ name: 'PackageCommission' })
const addDialogVisible = ref(false)
const loading = ref(false)
const addLoading = ref(false)
// 定义表单搜索初始值
const initialSearchState = {
cardNumber: '',
virtualNumber: '',
createDateRange: []
}
// 响应式表单数据
const formFilters = reactive({ ...initialSearchState })
const pagination = reactive({
currentPage: 1,
pageSize: 20,
total: 0
})
// 表格数据
const tableData = ref<any[]>([])
// 表格实例引用
const tableRef = ref()
// 选中的行数据
const selectedRows = ref<any[]>([])
// 新增表单实例
const addFormRef = ref<FormInstance>()
// 新增表单数据
const addFormData = reactive({
cardNumber: '',
month: ''
})
// 模拟数据
const mockData = [
{
id: 1,
cardNumber: '89860621370079892035',
accessNumber: '1440012345678',
virtualNumber: '100001',
cardCompany: '联通',
month: '2025-11',
createTime: '2025-11-08 10:30:00',
creator: '张若暄'
},
{
id: 2,
cardNumber: '89860621370079892036',
accessNumber: '1440012345679',
virtualNumber: '100002',
cardCompany: '移动',
month: '2025-11',
createTime: '2025-11-07 14:15:00',
creator: '孔丽娟'
},
{
id: 3,
cardNumber: '89860621370079892037',
accessNumber: '1440012345680',
virtualNumber: '100003',
cardCompany: '电信',
month: '2025-11',
createTime: '2025-11-06 09:45:00',
creator: '李佳音'
},
{
id: 4,
cardNumber: '89860621370079892038',
accessNumber: '1440012345681',
virtualNumber: '100004',
cardCompany: '联通',
month: '2025-10',
createTime: '2025-10-20 16:20:00',
creator: '赵强'
},
{
id: 5,
cardNumber: '89860621370079892039',
accessNumber: '1440012345682',
virtualNumber: '100005',
cardCompany: '广电',
month: '2025-10',
createTime: '2025-10-15 11:30:00',
creator: '张若暄'
}
]
// 重置表单
const handleReset = () => {
Object.assign(formFilters, { ...initialSearchState })
pagination.currentPage = 1
getCommissionCardList()
}
// 搜索处理
const handleSearch = () => {
console.log('搜索参数:', formFilters)
pagination.currentPage = 1
getCommissionCardList()
}
// 表单项变更处理
const handleFormChange = (params: SearchChangeParams): void => {
console.log('表单项变更:', params)
}
// 表单配置项
const formItems: SearchFormItem[] = [
{
label: '卡号',
prop: 'cardNumber',
type: 'input',
config: {
clearable: true,
placeholder: '请输入卡号'
},
onChange: handleFormChange
},
{
label: '虚拟号',
prop: 'virtualNumber',
type: 'input',
config: {
clearable: true,
placeholder: '请输入虚拟号'
},
onChange: handleFormChange
},
{
label: '创建时间',
prop: 'createDateRange',
type: 'daterange',
config: {
type: 'daterange',
startPlaceholder: '开始时间',
endPlaceholder: '结束时间'
},
onChange: handleFormChange
}
]
// 列配置
const columnOptions = [
{ label: '勾选', type: 'selection' },
{ label: '卡号', prop: 'cardNumber' },
{ label: '接入号', prop: 'accessNumber' },
{ label: '虚拟号', prop: 'virtualNumber' },
{ label: '开卡公司', prop: 'cardCompany' },
{ label: '月份', prop: 'month' },
{ label: '创建时间', prop: 'createTime' },
{ label: '创建人', prop: 'creator' },
{ label: '操作', prop: 'operation' }
]
// 导出Excel
const exportExcel = () => {
if (selectedRows.value.length === 0) {
ElMessage.warning('请先选择要导出的数据')
return
}
ElMessage.success(`导出 ${selectedRows.value.length} 条套餐佣金网卡记录`)
}
// 显示新增对话框
const showAddDialog = () => {
addDialogVisible.value = true
// 重置表单
if (addFormRef.value) {
addFormRef.value.resetFields()
}
addFormData.cardNumber = ''
addFormData.month = ''
}
// 查看详情
const viewDetails = (row: any) => {
ElMessage.info(`查看详情: ${row.cardNumber}`)
}
// 编辑记录
const editRecord = (row: any) => {
ElMessage.info(`编辑记录: ${row.cardNumber}`)
}
// 删除记录
const deleteRecord = (row: any) => {
ElMessageBox.confirm(`确定要删除卡号为"${row.cardNumber}"的佣金记录吗?`, '删除确认', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
})
.then(() => {
ElMessage.success('删除成功')
getCommissionCardList()
})
.catch(() => {
ElMessage.info('已取消删除')
})
}
// 动态列配置
const { columnChecks, columns } = useCheckedColumns(() => [
{ type: 'selection' },
{
prop: 'cardNumber',
label: '卡号',
minWidth: 180
},
{
prop: 'accessNumber',
label: '接入号',
width: 140
},
{
prop: 'virtualNumber',
label: '虚拟号',
width: 100
},
{
prop: 'cardCompany',
label: '开卡公司',
width: 100
},
{
prop: 'month',
label: '月份',
width: 100
},
{
prop: 'createTime',
label: '创建时间',
width: 160
},
{
prop: 'creator',
label: '创建人',
width: 100
},
{
prop: 'operation',
label: '操作',
width: 220,
formatter: (row: any) => {
return h('div', { class: 'operation-buttons' }, [
h(ArtButtonTable, {
text: '查看',
onClick: () => viewDetails(row)
}),
h(ArtButtonTable, {
text: '编辑',
onClick: () => editRecord(row)
}),
h(ArtButtonTable, {
text: '删除',
onClick: () => deleteRecord(row)
})
])
}
}
])
onMounted(() => {
getCommissionCardList()
})
// 获取套餐佣金网卡列表
const getCommissionCardList = async () => {
loading.value = true
try {
// 模拟API调用
await new Promise((resolve) => setTimeout(resolve, 500))
const startIndex = (pagination.currentPage - 1) * pagination.pageSize
const endIndex = startIndex + pagination.pageSize
const paginatedData = mockData.slice(startIndex, endIndex)
tableData.value = paginatedData
pagination.total = mockData.length
loading.value = false
} catch (error) {
console.error('获取套餐佣金网卡列表失败:', error)
loading.value = false
}
}
const handleRefresh = () => {
getCommissionCardList()
}
// 处理表格行选择变化
const handleSelectionChange = (selection: any[]) => {
selectedRows.value = selection
}
// 处理表格分页变化
const handleSizeChange = (newPageSize: number) => {
pagination.pageSize = newPageSize
getCommissionCardList()
}
const handleCurrentChange = (newCurrentPage: number) => {
pagination.currentPage = newCurrentPage
getCommissionCardList()
}
// 新增表单验证规则
const addRules = reactive<FormRules>({
cardNumber: [
{ required: true, message: '请输入卡号', trigger: 'blur' },
{ min: 15, max: 20, message: '卡号长度在 15 到 20 个字符', trigger: 'blur' }
],
month: [{ required: true, message: '请选择月份', trigger: 'change' }]
})
// 提交新增
const handleAddSubmit = async () => {
if (!addFormRef.value) return
await addFormRef.value.validate((valid) => {
if (valid) {
addLoading.value = true
// 模拟新增过程
setTimeout(() => {
ElMessage.success(
`新增套餐佣金网卡成功!卡号:${addFormData.cardNumber},月份:${addFormData.month}`
)
addDialogVisible.value = false
addLoading.value = false
getCommissionCardList()
}, 2000)
}
})
}
</script>
<style lang="scss" scoped>
.package-commission-page {
// 可以添加特定样式
}
:deep(.operation-buttons) {
display: flex;
gap: 8px;
}
.dialog-footer {
text-align: right;
}
:deep(.el-form-item) {
margin-bottom: 20px;
}
</style>

View File

@@ -0,0 +1,953 @@
<template>
<ArtTableFullScreen>
<div class="package-create-page" id="table-full-screen">
<!-- 搜索栏 -->
<ArtSearchBar
v-model:filter="formFilters"
:items="formItems"
@reset="handleReset"
@search="handleSearch"
></ArtSearchBar>
<ElCard shadow="never" class="art-table-card">
<!-- 数据视图组件 -->
<ArtDataViewer
ref="dataViewerRef"
:data="tableData"
:loading="loading"
:table-columns="columns"
:descriptions-fields="descriptionsFields"
:descriptions-columns="2"
:pagination="pagination"
:label-width="'140px'"
:field-columns="columnChecks"
:show-card-actions="true"
:show-card-selection="true"
:default-view="currentView"
@selection-change="handleSelectionChange"
@size-change="handleSizeChange"
@current-change="handleCurrentChange"
@view-change="handleViewChange"
>
<template #header-left>
<ElButton type="success" @click="showCreateDialog">新增套餐</ElButton>
<ElButton @click="exportExcel">导出excel</ElButton>
</template>
<template #header-right>
<ArtTableHeader
:columnList="columnOptions"
v-model:columns="columnChecks"
v-model:currentView="currentView"
@refresh="handleRefresh"
@viewChange="handleViewChange"
:show-title="false"
:show-view-toggle="true"
/>
</template>
<template #card-actions="{ item }">
<ElButton type="primary" size="small" @click="editPackage(item)">
编辑
</ElButton>
<ElButton type="warning" size="small" @click="offlinePackage(item)">
下架
</ElButton>
<ElButton type="success" size="small" @click="copyPackage(item)">
复制套餐
</ElButton>
</template>
</ArtDataViewer>
<!-- 新增套餐对话框 -->
<ElDialog
v-model="dialogVisible"
title="新增套餐"
width="800px"
align-center
:close-on-click-modal="false"
>
<ElForm ref="formRef" :model="formData" :rules="rules" label-width="140px">
<ElRow :gutter="24">
<ElCol :span="12">
<ElFormItem label="套餐系列" prop="series">
<ElSelect
v-model="formData.series"
placeholder="请选择套餐系列"
style="width: 100%"
>
<ElOption label="畅玩系列" value="changwan" />
<ElOption label="如意系列" value="ruyi" />
<ElOption label="NB专享" value="nb_special" />
<ElOption label="大流量系列" value="big_data" />
<ElOption label="广电系列" value="gdtv" />
</ElSelect>
</ElFormItem>
</ElCol>
<ElCol :span="12">
<ElFormItem label="余额充值梯度" prop="balanceRechargeGradient">
<ElSelect
v-model="formData.balanceRechargeGradient"
placeholder="请选择充值梯度"
style="width: 100%"
>
<ElOption label="10,20,50,100" value="10,20,50,100" />
<ElOption label="5,10,20,50" value="5,10,20,50" />
<ElOption label="10,50,100" value="10,50,100" />
<ElOption label="20,50,100,200" value="20,50,100,200" />
<ElOption label="10,30,50" value="10,30,50" />
</ElSelect>
</ElFormItem>
</ElCol>
</ElRow>
<ElRow :gutter="24">
<ElCol :span="12">
<ElFormItem label="套餐名称" prop="name">
<ElInput v-model="formData.name" placeholder="请输入套餐名称" />
</ElFormItem>
</ElCol>
<ElCol :span="12">
<ElFormItem label="套餐类型" prop="type">
<ElSelect
v-model="formData.type"
placeholder="请选择套餐类型"
style="width: 100%"
>
<ElOption label="月套餐" value="monthly" />
<ElOption label="年套餐" value="yearly" />
<ElOption label="流量包" value="data_package" />
<ElOption label="季度套餐" value="quarterly" />
</ElSelect>
</ElFormItem>
</ElCol>
</ElRow>
<ElRow :gutter="24">
<ElCol :span="12">
<ElFormItem label="本月次月加餐包" prop="monthlyNextMonthAddon">
<ElSelect
v-model="formData.monthlyNextMonthAddon"
placeholder="请选择加餐包支持"
style="width: 100%"
>
<ElOption label="支持" value="support" />
<ElOption label="不支持" value="not_support" />
</ElSelect>
</ElFormItem>
</ElCol>
<ElCol :span="12">
<ElFormItem label="本月最大充值数量" prop="maxMonthlyRecharge">
<ElInputNumber
v-model="formData.maxMonthlyRecharge"
:min="1"
:max="1000"
placeholder="请输入本月最大充值数量"
style="width: 100%"
/>
</ElFormItem>
</ElCol>
</ElRow>
<ElRow :gutter="24">
<ElCol :span="12">
<ElFormItem label="次月最大充值数量" prop="maxNextMonthRecharge">
<ElInputNumber
v-model="formData.maxNextMonthRecharge"
:min="1"
:max="1000"
placeholder="请输入次月最大充值数量"
style="width: 100%"
/>
</ElFormItem>
</ElCol>
<ElCol :span="12">
<ElFormItem label="套餐数量" prop="packageCount">
<ElInputNumber
v-model="formData.packageCount"
:min="1"
:max="999999"
placeholder="请输入套餐数量"
style="width: 100%"
/>
</ElFormItem>
</ElCol>
</ElRow>
<ElRow :gutter="24">
<ElCol :span="12">
<ElFormItem label="成本价" prop="costPrice">
<ElInputNumber
v-model="formData.costPrice"
:min="0"
:max="9999"
:precision="2"
placeholder="请输入成本价"
style="width: 100%"
/>
</ElFormItem>
</ElCol>
<ElCol :span="12">
<ElFormItem label="套餐金额" prop="packageAmount">
<ElInputNumber
v-model="formData.packageAmount"
:min="0"
:max="9999"
:precision="2"
placeholder="请输入套餐金额"
style="width: 100%"
/>
</ElFormItem>
</ElCol>
</ElRow>
</ElForm>
<template #footer>
<div class="dialog-footer">
<ElButton @click="dialogVisible = false">取消</ElButton>
<ElButton type="primary" @click="handleSubmit" :loading="submitLoading">
确认新增
</ElButton>
</div>
</template>
</ElDialog>
</ElCard>
</div>
</ArtTableFullScreen>
</template>
<script setup lang="ts">
import { h } from 'vue'
import { ElTag, ElMessage, ElMessageBox, ElInputNumber } from 'element-plus'
import type { FormInstance, FormRules } from 'element-plus'
import { useCheckedColumns } from '@/composables/useCheckedColumns'
import ArtButtonTable from '@/components/core/forms/ArtButtonTable.vue'
import ArtDataViewer from '@/components/core/views/ArtDataViewer.vue'
import { SearchChangeParams, SearchFormItem } from '@/types'
defineOptions({ name: 'PackageCreate' })
const dialogVisible = ref(false)
const loading = ref(false)
const submitLoading = ref(false)
// 当前视图模式
const currentView = ref('table')
// 数据视图组件引用
const dataViewerRef = ref()
// 定义表单搜索初始值
const initialSearchState = {
packageType: '',
packageName: '',
packageCode: '',
status: '',
packageSeries: '',
levelOneMerge: '',
levelTwoMerge: ''
}
// 响应式表单数据
const formFilters = reactive({ ...initialSearchState })
const pagination = reactive({
currentPage: 1,
pageSize: 20,
total: 0
})
// 表格数据
const tableData = ref<any[]>([])
// 表格实例引用
const tableRef = ref()
// 选中的行数据
const selectedRows = ref<any[]>([])
// 表单实例
const formRef = ref<FormInstance>()
// 新增表单数据
const formData = reactive({
series: '',
balanceRechargeGradient: '',
name: '',
type: '',
monthlyNextMonthAddon: '',
maxMonthlyRecharge: 0,
maxNextMonthRecharge: 0,
packageCount: 0,
costPrice: 0,
packageAmount: 0
})
// 模拟数据
const mockData = [
{
id: 1,
packageSeries: '畅玩系列',
packageName: '随意联畅玩年卡套餐',
packageTraffic: 120,
packageCount: 1000,
packageCountType: '张',
packageType: '年套餐',
packageAmount: '168.00',
description: '年卡套餐含120GB流量',
frontendSort: 1,
sort: 1,
maxMonthlyRecharge: 50,
maxNextMonthRecharge: 50,
monthlyNextMonthAddon: '支持',
status: '启用',
levelOneMerge: '是',
levelOneMergeCode: 'L1_001',
levelTwoMerge: '否',
levelTwoMergeCode: '',
isSpecial: '否',
creator: '张若暄',
modifier: '张若暄',
addTime: '2025-11-08 10:30:00',
balanceRechargeGradient: '10,20,50,100',
packageCode: 'PKG_001'
},
{
id: 2,
packageSeries: '如意系列',
packageName: '如意包年3G流量包',
packageTraffic: 36,
packageCount: 800,
packageCountType: '张',
packageType: '年套餐',
packageAmount: '98.00',
description: '年包3GB月套餐',
frontendSort: 2,
sort: 2,
maxMonthlyRecharge: 30,
maxNextMonthRecharge: 30,
monthlyNextMonthAddon: '支持',
status: '启用',
levelOneMerge: '是',
levelOneMergeCode: 'L1_002',
levelTwoMerge: '是',
levelTwoMergeCode: 'L2_001',
isSpecial: '否',
creator: '孔丽娟',
modifier: '孔丽娟',
addTime: '2025-11-07 14:15:00',
balanceRechargeGradient: '5,10,20,50',
packageCode: 'PKG_002'
},
{
id: 3,
packageSeries: 'NB专享',
packageName: 'Y-NB专享套餐',
packageTraffic: 24,
packageCount: 500,
packageCountType: '张',
packageType: '月套餐',
packageAmount: '25.00',
description: 'NB-IoT专用套餐',
frontendSort: 3,
sort: 3,
maxMonthlyRecharge: 100,
maxNextMonthRecharge: 100,
monthlyNextMonthAddon: '不支持',
status: '停用',
levelOneMerge: '否',
levelOneMergeCode: '',
levelTwoMerge: '否',
levelTwoMergeCode: '',
isSpecial: '是',
creator: '李佳音',
modifier: '李佳音',
addTime: '2025-11-06 09:45:00',
balanceRechargeGradient: '10,50,100',
packageCode: 'PKG_003'
},
{
id: 4,
packageSeries: '大流量系列',
packageName: '100G全国流量月卡套餐',
packageTraffic: 100,
packageCount: 1200,
packageCountType: '张',
packageType: '月套餐',
packageAmount: '59.00',
description: '100GB大流量月卡',
frontendSort: 4,
sort: 4,
maxMonthlyRecharge: 80,
maxNextMonthRecharge: 80,
monthlyNextMonthAddon: '支持',
status: '启用',
levelOneMerge: '是',
levelOneMergeCode: 'L1_003',
levelTwoMerge: '否',
levelTwoMergeCode: '',
isSpecial: '否',
creator: '赵强',
modifier: '赵强',
addTime: '2025-11-05 16:20:00',
balanceRechargeGradient: '20,50,100,200',
packageCode: 'PKG_004'
},
{
id: 5,
packageSeries: '广电系列',
packageName: '广电飞悦卡无预存50G',
packageTraffic: 50,
packageCount: 600,
packageCountType: '张',
packageType: '月套餐',
packageAmount: '39.00',
description: '广电30天50GB流量',
frontendSort: 5,
sort: 5,
maxMonthlyRecharge: 60,
maxNextMonthRecharge: 60,
monthlyNextMonthAddon: '支持',
status: '启用',
levelOneMerge: '否',
levelOneMergeCode: '',
levelTwoMerge: '是',
levelTwoMergeCode: 'L2_002',
isSpecial: '是',
creator: '张若暄',
modifier: '张若暄',
addTime: '2025-11-04 11:30:00',
balanceRechargeGradient: '10,30,50',
packageCode: 'PKG_005'
}
]
// 重置表单
const handleReset = () => {
Object.assign(formFilters, { ...initialSearchState })
pagination.currentPage = 1
getPackageList()
}
// 搜索处理
const handleSearch = () => {
console.log('搜索参数:', formFilters)
pagination.currentPage = 1
getPackageList()
}
// 表单项变更处理
const handleFormChange = (params: SearchChangeParams): void => {
console.log('表单项变更:', params)
}
// 表单配置项
const formItems: SearchFormItem[] = [
{
label: '套餐类型',
prop: 'packageType',
type: 'select',
config: {
clearable: true,
placeholder: '全部'
},
options: () => [
{ label: '月套餐', value: 'monthly' },
{ label: '年套餐', value: 'yearly' },
{ label: '流量包', value: 'data_package' },
{ label: '季度套餐', value: 'quarterly' }
],
onChange: handleFormChange
},
{
label: '套餐名称',
prop: 'packageName',
type: 'input',
config: {
clearable: true,
placeholder: '请输入套餐名称'
},
onChange: handleFormChange
},
{
label: '套餐编码',
prop: 'packageCode',
type: 'input',
config: {
clearable: true,
placeholder: '请输入套餐编码'
},
onChange: handleFormChange
},
{
label: '状态',
prop: 'status',
type: 'select',
config: {
clearable: true,
placeholder: '全部'
},
options: () => [
{ label: '启用', value: 'active' },
{ label: '停用', value: 'inactive' }
],
onChange: handleFormChange
},
{
label: '套餐系列',
prop: 'packageSeries',
type: 'input',
config: {
clearable: true,
placeholder: '请输入套餐系列'
},
onChange: handleFormChange
},
{
label: '一级合并',
prop: 'levelOneMerge',
type: 'select',
config: {
clearable: true,
placeholder: '全部'
},
options: () => [
{ label: '是', value: 'yes' },
{ label: '否', value: 'no' }
],
onChange: handleFormChange
},
{
label: '二级合并',
prop: 'levelTwoMerge',
type: 'select',
config: {
clearable: true,
placeholder: '全部'
},
options: () => [
{ label: '是', value: 'yes' },
{ label: '否', value: 'no' }
],
onChange: handleFormChange
}
]
// 列配置
const columnOptions = [
{ label: '勾选', type: 'selection' },
{ label: '套餐系列', prop: 'packageSeries' },
{ label: '套餐名称', prop: 'packageName' },
{ label: '套餐流量(GB)', prop: 'packageTraffic' },
{ label: '套餐数量', prop: 'packageCount' },
{ label: '套餐数量类型', prop: 'packageCountType' },
{ label: '套餐类型', prop: 'packageType' },
{ label: '套餐金额', prop: 'packageAmount' },
{ label: '描述', prop: 'description' },
{ label: '前端排序', prop: 'frontendSort' },
{ label: '排序', prop: 'sort' },
{ label: '本月最大充值数量', prop: 'maxMonthlyRecharge' },
{ label: '次月最大充值数量', prop: 'maxNextMonthRecharge' },
{ label: '本月次月加餐包', prop: 'monthlyNextMonthAddon' },
{ label: '状态', prop: 'status' },
{ label: '一级是否合并', prop: 'levelOneMerge' },
{ label: '一级合并编号', prop: 'levelOneMergeCode' },
{ label: '二级是否合并', prop: 'levelTwoMerge' },
{ label: '二级合并编号', prop: 'levelTwoMergeCode' },
{ label: '是否特殊', prop: 'isSpecial' },
{ label: '创建人', prop: 'creator' },
{ label: '修改人', prop: 'modifier' },
{ label: '添加时间', prop: 'addTime' },
{ label: '余额充值梯度', prop: 'balanceRechargeGradient' },
{ label: '套餐编码', prop: 'packageCode' },
{ label: '操作', prop: 'operation' }
]
// 描述字段配置(用于卡片视图)
const descriptionsFields = [
{ prop: 'packageName', label: '套餐名称', span: 2 },
{ prop: 'packageSeries', label: '套餐系列' },
{ prop: 'packageCode', label: '套餐编码' },
{
prop: 'packageTraffic',
label: '套餐流量',
formatter: (row: any) => `${row.packageTraffic}GB`
},
{
prop: 'packageCount',
label: '套餐数量',
formatter: (row: any) => `${row.packageCount}${row.packageCountType}`
},
{ prop: 'packageType', label: '套餐类型' },
{
prop: 'packageAmount',
label: '套餐金额',
formatter: (row: any) => `¥${row.packageAmount}`
},
{ prop: 'description', label: '描述', span: 2 },
{
prop: 'status',
label: '状态',
formatter: (row: any) => {
const type = getStatusType(row.status)
return `<el-tag type="${type}" size="small">${row.status}</el-tag>`
}
},
{
prop: 'monthlyNextMonthAddon',
label: '本月次月加餐包',
formatter: (row: any) => {
const type = row.monthlyNextMonthAddon === '支持' ? 'success' : 'info'
return `<el-tag type="${type}" size="small">${row.monthlyNextMonthAddon}</el-tag>`
}
},
{ prop: 'maxMonthlyRecharge', label: '本月最大充值数量' },
{ prop: 'maxNextMonthRecharge', label: '次月最大充值数量' },
{
prop: 'levelOneMerge',
label: '一级是否合并',
formatter: (row: any) => {
const type = getMergeType(row.levelOneMerge)
return `<el-tag type="${type}" size="small">${row.levelOneMerge}</el-tag>`
}
},
{ prop: 'levelOneMergeCode', label: '一级合并编号', formatter: (row: any) => row.levelOneMergeCode || '-' },
{
prop: 'levelTwoMerge',
label: '二级是否合并',
formatter: (row: any) => {
const type = getMergeType(row.levelTwoMerge)
return `<el-tag type="${type}" size="small">${row.levelTwoMerge}</el-tag>`
}
},
{ prop: 'levelTwoMergeCode', label: '二级合并编号', formatter: (row: any) => row.levelTwoMergeCode || '-' },
{
prop: 'isSpecial',
label: '是否特殊',
formatter: (row: any) => {
const type = getSpecialType(row.isSpecial)
return `<el-tag type="${type}" size="small">${row.isSpecial}</el-tag>`
}
},
{ prop: 'creator', label: '创建人' },
{ prop: 'addTime', label: '添加时间', span: 2 },
{ prop: 'balanceRechargeGradient', label: '余额充值梯度' },
{ prop: 'frontendSort', label: '前端排序' },
{ prop: 'sort', label: '排序' }
]
// 获取状态标签类型
const getStatusType = (status: string) => {
switch (status) {
case '启用':
return 'success'
case '停用':
return 'danger'
default:
return 'info'
}
}
// 获取是否合并标签类型
const getMergeType = (merge: string) => {
switch (merge) {
case '是':
return 'success'
case '否':
return 'info'
default:
return 'info'
}
}
// 获取是否特殊标签类型
const getSpecialType = (special: string) => {
switch (special) {
case '是':
return 'warning'
case '否':
return 'info'
default:
return 'info'
}
}
// 导出Excel
const exportExcel = () => {
if (selectedRows.value.length === 0) {
ElMessage.warning('请先选择要导出的数据')
return
}
ElMessage.success(`导出 ${selectedRows.value.length} 条套餐记录`)
}
// 显示新增对话框
const showCreateDialog = () => {
dialogVisible.value = true
// 重置表单
if (formRef.value) {
formRef.value.resetFields()
}
formData.series = ''
formData.balanceRechargeGradient = ''
formData.name = ''
formData.type = ''
formData.monthlyNextMonthAddon = ''
formData.maxMonthlyRecharge = 0
formData.maxNextMonthRecharge = 0
formData.packageCount = 0
formData.costPrice = 0
formData.packageAmount = 0
}
// 编辑套餐
const editPackage = (row: any) => {
ElMessage.info(`编辑套餐: ${row.packageName}`)
}
// 下架套餐
const offlinePackage = (row: any) => {
ElMessageBox.confirm(`确定要下架套餐"${row.packageName}"吗?`, '下架确认', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
})
.then(() => {
ElMessage.success('套餐下架成功')
getPackageList()
})
.catch(() => {
ElMessage.info('已取消下架')
})
}
// 复制套餐
const copyPackage = (row: any) => {
ElMessage.info(`复制套餐: ${row.packageName}`)
}
// 动态列配置
const { columnChecks, columns } = useCheckedColumns(() => [
{ type: 'selection' },
{
prop: 'packageSeries',
label: '套餐系列',
width: 120
},
{
prop: 'packageName',
label: '套餐名称',
minWidth: 180
},
{
prop: 'packageTraffic',
label: '套餐流量(GB)',
width: 120,
formatter: (row) => `${row.packageTraffic}GB`
},
{
prop: 'packageCount',
label: '套餐数量',
width: 100,
formatter: (row) => `${row.packageCount}${row.packageCountType}`
},
{
prop: 'packageType',
label: '套餐类型',
width: 100
},
{
prop: 'packageAmount',
label: '套餐金额',
width: 100,
formatter: (row) => `¥${row.packageAmount}`
},
{
prop: 'status',
label: '状态',
width: 80,
formatter: (row) => {
return h(ElTag, { type: getStatusType(row.status) }, () => row.status)
}
},
{
prop: 'levelOneMerge',
label: '一级是否合并',
width: 120,
formatter: (row) => {
return h(ElTag, { type: getMergeType(row.levelOneMerge) }, () => row.levelOneMerge)
}
},
{
prop: 'levelOneMergeCode',
label: '一级合并编号',
width: 120,
formatter: (row) => row.levelOneMergeCode || '-'
},
{
prop: 'levelTwoMerge',
label: '二级是否合并',
width: 120,
formatter: (row) => {
return h(ElTag, { type: getMergeType(row.levelTwoMerge) }, () => row.levelTwoMerge)
}
},
{
prop: 'levelTwoMergeCode',
label: '二级合并编号',
width: 120,
formatter: (row) => row.levelTwoMergeCode || '-'
},
{
prop: 'isSpecial',
label: '是否特殊',
width: 100,
formatter: (row) => {
return h(ElTag, { type: getSpecialType(row.isSpecial) }, () => row.isSpecial)
}
},
{
prop: 'creator',
label: '创建人',
width: 100
},
{
prop: 'addTime',
label: '添加时间',
width: 160
},
{
prop: 'packageCode',
label: '套餐编码',
width: 120
},
{
prop: 'operation',
label: '操作',
width: 240,
formatter: (row: any) => {
return h('div', { class: 'operation-buttons' }, [
h(ArtButtonTable, {
text: '编辑',
onClick: () => editPackage(row)
}),
h(ArtButtonTable, {
text: '下架',
onClick: () => offlinePackage(row)
}),
h(ArtButtonTable, {
text: '复制套餐',
onClick: () => copyPackage(row)
})
])
}
}
])
onMounted(() => {
getPackageList()
})
// 获取套餐列表
const getPackageList = async () => {
loading.value = true
try {
// 模拟API调用
await new Promise((resolve) => setTimeout(resolve, 500))
const startIndex = (pagination.currentPage - 1) * pagination.pageSize
const endIndex = startIndex + pagination.pageSize
const paginatedData = mockData.slice(startIndex, endIndex)
tableData.value = paginatedData
pagination.total = mockData.length
loading.value = false
} catch (error) {
console.error('获取套餐列表失败:', error)
loading.value = false
}
}
const handleRefresh = () => {
getPackageList()
}
// 处理表格行选择变化
const handleSelectionChange = (selection: any[]) => {
selectedRows.value = selection
}
// 处理表格分页变化
const handleSizeChange = (newPageSize: number) => {
pagination.pageSize = newPageSize
getPackageList()
}
const handleCurrentChange = (newCurrentPage: number) => {
pagination.currentPage = newCurrentPage
getPackageList()
}
// 处理视图切换
const handleViewChange = (view: string) => {
console.log('视图切换到:', view)
// 可以在这里添加视图切换的额外逻辑,比如保存用户偏好
}
// 表单验证规则
const rules = reactive<FormRules>({
series: [{ required: true, message: '请选择套餐系列', trigger: 'change' }],
balanceRechargeGradient: [{ required: true, message: '请选择余额充值梯度', trigger: 'change' }],
name: [
{ required: true, message: '请输入套餐名称', trigger: 'blur' },
{ min: 2, max: 50, message: '套餐名称长度在 2 到 50 个字符', trigger: 'blur' }
],
type: [{ required: true, message: '请选择套餐类型', trigger: 'change' }],
monthlyNextMonthAddon: [{ required: true, message: '请选择本月次月加餐包', trigger: 'change' }],
maxMonthlyRecharge: [{ required: true, message: '请输入本月最大充值数量', trigger: 'blur' }],
maxNextMonthRecharge: [{ required: true, message: '请输入次月最大充值数量', trigger: 'blur' }],
packageCount: [{ required: true, message: '请输入套餐数量', trigger: 'blur' }],
costPrice: [{ required: true, message: '请输入成本价', trigger: 'blur' }],
packageAmount: [{ required: true, message: '请输入套餐金额', trigger: 'blur' }]
})
// 提交表单
const handleSubmit = async () => {
if (!formRef.value) return
await formRef.value.validate((valid) => {
if (valid) {
submitLoading.value = true
// 模拟提交过程
setTimeout(() => {
ElMessage.success(
`新增套餐成功!套餐名称:${formData.name},套餐系列:${formData.series}`
)
dialogVisible.value = false
submitLoading.value = false
getPackageList()
}, 2000)
}
})
}
</script>
<style lang="scss" scoped>
.package-create-page {
// 可以添加特定样式
}
:deep(.operation-buttons) {
display: flex;
gap: 8px;
}
.dialog-footer {
text-align: right;
}
:deep(.el-form-item) {
margin-bottom: 20px;
}
</style>

View File

@@ -0,0 +1,510 @@
<template>
<ArtTableFullScreen>
<div class="package-list-page" id="table-full-screen">
<!-- 搜索栏 -->
<ArtSearchBar
v-model:filter="formFilters"
:items="formItems"
@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="handleSearch">查询</ElButton>
<ElButton @click="exportExcel">导出excel</ElButton>
</template>
</ArtTableHeader>
<!-- 表格 -->
<ArtTable
ref="tableRef"
row-key="id"
:loading="loading"
:data="tableData"
:currentPage="pagination.currentPage"
:pageSize="pagination.pageSize"
:total="pagination.total"
:marginTop="10"
@selection-change="handleSelectionChange"
@size-change="handleSizeChange"
@current-change="handleCurrentChange"
>
<template #default>
<ElTableColumn v-for="col in columns" :key="col.prop || col.type" v-bind="col" />
</template>
</ArtTable>
</ElCard>
</div>
</ArtTableFullScreen>
</template>
<script setup lang="ts">
import { h } from 'vue'
import { ElTag, ElMessageBox, ElMessage } from 'element-plus'
import { useCheckedColumns } from '@/composables/useCheckedColumns'
import ArtButtonTable from '@/components/core/forms/ArtButtonTable.vue'
import { SearchChangeParams, SearchFormItem } from '@/types'
defineOptions({ name: 'PackageList' })
const loading = ref(false)
// 定义表单搜索初始值
const initialSearchState = {
agentName: '',
packageType: '',
status: '',
packageName: '',
category: '',
agentAccount: '',
packageSeries: ''
}
// 响应式表单数据
const formFilters = reactive({ ...initialSearchState })
const pagination = reactive({
currentPage: 1,
pageSize: 20,
total: 0
})
// 表格数据
const tableData = ref<any[]>([])
// 表格实例引用
const tableRef = ref()
// 选中的行数据
const selectedRows = ref<any[]>([])
// 模拟数据
const mockData = [
{
id: 1,
packageSeries: '畅玩系列',
agentName: '北京优享科技有限公司',
agentAccount: 'BJ_YXKJ_001',
packageName: '随意联畅玩年卡套餐',
packageTraffic: 120,
packageType: '年套餐',
costPrice: 120.0,
officialSuggestedPrice: 168.0,
mySalesAmount: 158.0,
sort: 1,
category: '物联网套餐',
packageCode: 'PKG_001',
status: '上架'
},
{
id: 2,
packageSeries: '如意系列',
agentName: '上海智联通信技术公司',
agentAccount: 'SH_ZLTX_002',
packageName: '如意包年3G流量包',
packageTraffic: 36,
packageType: '年套餐',
costPrice: 80.0,
officialSuggestedPrice: 98.0,
mySalesAmount: 95.0,
sort: 2,
category: '流量套餐',
packageCode: 'PKG_002',
status: '下架'
},
{
id: 3,
packageSeries: 'NB专享',
agentName: '广州物联网络有限公司',
agentAccount: 'GZ_WLWL_003',
packageName: 'Y-NB专享套餐',
packageTraffic: 24,
packageType: '月套餐',
costPrice: 20.0,
officialSuggestedPrice: 25.0,
mySalesAmount: 24.0,
sort: 3,
category: 'NB-IoT套餐',
packageCode: 'PKG_003',
status: '上架'
},
{
id: 4,
packageSeries: '大流量系列',
agentName: '深圳云联科技股份公司',
agentAccount: 'SZ_YLKJ_004',
packageName: '100G全国流量月卡套餐',
packageTraffic: 100,
packageType: '月套餐',
costPrice: 45.0,
officialSuggestedPrice: 59.0,
mySalesAmount: 55.0,
sort: 4,
category: '大流量套餐',
packageCode: 'PKG_004',
status: '上架'
},
{
id: 5,
packageSeries: '广电系列',
agentName: '杭州通达网络服务公司',
agentAccount: 'HZ_TDWL_005',
packageName: '广电飞悦卡无预存50G',
packageTraffic: 50,
packageType: '月套餐',
costPrice: 30.0,
officialSuggestedPrice: 39.0,
mySalesAmount: 36.0,
sort: 5,
category: '广电套餐',
packageCode: 'PKG_005',
status: '下架'
}
]
// 重置表单
const handleReset = () => {
Object.assign(formFilters, { ...initialSearchState })
pagination.currentPage = 1
getPackageList()
}
// 搜索处理
const handleSearch = () => {
console.log('搜索参数:', formFilters)
pagination.currentPage = 1
getPackageList()
}
// 表单项变更处理
const handleFormChange = (params: SearchChangeParams): void => {
console.log('表单项变更:', params)
}
// 表单配置项
const formItems: SearchFormItem[] = [
{
label: '代理商名称',
prop: 'agentName',
type: 'input',
config: {
clearable: true,
placeholder: '请输入代理商名称'
},
onChange: handleFormChange
},
{
label: '套餐类型',
prop: 'packageType',
type: 'select',
config: {
clearable: true,
placeholder: '全部'
},
options: () => [
{ label: '月套餐', value: 'monthly' },
{ label: '年套餐', value: 'yearly' },
{ label: '流量包', value: 'data_package' },
{ label: '季度套餐', value: 'quarterly' }
],
onChange: handleFormChange
},
{
label: '状态',
prop: 'status',
type: 'select',
config: {
clearable: true,
placeholder: '全部'
},
options: () => [
{ label: '上架', value: 'online' },
{ label: '下架', value: 'offline' }
],
onChange: handleFormChange
},
{
label: '套餐名称',
prop: 'packageName',
type: 'input',
config: {
clearable: true,
placeholder: '请输入套餐名称'
},
onChange: handleFormChange
},
{
label: '类别',
prop: 'category',
type: 'select',
config: {
clearable: true,
placeholder: '全部'
},
options: () => [
{ label: '物联网套餐', value: 'iot_package' },
{ label: '流量套餐', value: 'data_package' },
{ label: 'NB-IoT套餐', value: 'nb_iot_package' },
{ label: '大流量套餐', value: 'big_data_package' },
{ label: '广电套餐', value: 'gdtv_package' }
],
onChange: handleFormChange
},
{
label: '代理商账号',
prop: 'agentAccount',
type: 'input',
config: {
clearable: true,
placeholder: '请输入代理商账号'
},
onChange: handleFormChange
},
{
label: '套餐系列',
prop: 'packageSeries',
type: 'input',
config: {
clearable: true,
placeholder: '请输入套餐系列'
},
onChange: handleFormChange
}
]
// 列配置
const columnOptions = [
{ label: '勾选', type: 'selection' },
{ label: '套餐系列', prop: 'packageSeries' },
{ label: '代理商名称', prop: 'agentName' },
{ label: '代理商账号', prop: 'agentAccount' },
{ label: '套餐名称', prop: 'packageName' },
{ label: '套餐流量(GB)', prop: 'packageTraffic' },
{ label: '套餐类型', prop: 'packageType' },
{ label: '套餐成本价', prop: 'costPrice' },
{ label: '官方建议销售价格', prop: 'officialSuggestedPrice' },
{ label: '我的销售金额', prop: 'mySalesAmount' },
{ label: '排序', prop: 'sort' },
{ label: '所属类别', prop: 'category' },
{ label: '套餐编码', prop: 'packageCode' },
{ label: '状态', prop: 'status' },
{ label: '操作', prop: 'operation' }
]
// 获取状态标签类型
const getStatusType = (status: string) => {
switch (status) {
case '上架':
return 'success'
case '下架':
return 'danger'
default:
return 'info'
}
}
// 导出Excel
const exportExcel = () => {
if (selectedRows.value.length === 0) {
ElMessage.warning('请先选择要导出的数据')
return
}
ElMessage.success(`导出 ${selectedRows.value.length} 条套餐记录`)
}
// 编辑套餐
const editPackage = (row: any) => {
ElMessage.info(`编辑套餐: ${row.packageName}`)
}
// 上架套餐
const onlinePackage = (row: any) => {
if (row.status === '上架') {
ElMessage.warning('该套餐已经上架')
return
}
ElMessageBox.confirm(`确定要上架套餐"${row.packageName}"吗?`, '上架确认', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'info'
})
.then(() => {
ElMessage.success('套餐上架成功')
getPackageList()
})
.catch(() => {
ElMessage.info('已取消上架')
})
}
// 下架套餐
const offlinePackage = (row: any) => {
if (row.status === '下架') {
ElMessage.warning('该套餐已经下架')
return
}
ElMessageBox.confirm(`确定要下架套餐"${row.packageName}"吗?`, '下架确认', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
})
.then(() => {
ElMessage.success('套餐下架成功')
getPackageList()
})
.catch(() => {
ElMessage.info('已取消下架')
})
}
// 动态列配置
const { columnChecks, columns } = useCheckedColumns(() => [
{ type: 'selection' },
{
prop: 'packageSeries',
label: '套餐系列',
width: 120
},
{
prop: 'agentName',
label: '代理商名称',
minWidth: 200
},
{
prop: 'agentAccount',
label: '代理商账号',
width: 140
},
{
prop: 'packageName',
label: '套餐名称',
minWidth: 180
},
{
prop: 'packageTraffic',
label: '套餐流量(GB)',
width: 120,
formatter: (row) => `${row.packageTraffic}GB`
},
{
prop: 'packageType',
label: '套餐类型',
width: 100
},
{
prop: 'costPrice',
label: '套餐成本价',
width: 110,
formatter: (row) => `¥${row.costPrice.toFixed(2)}`
},
{
prop: 'officialSuggestedPrice',
label: '官方建议销售价格',
width: 150,
formatter: (row) => `¥${row.officialSuggestedPrice.toFixed(2)}`
},
{
prop: 'mySalesAmount',
label: '我的销售金额',
width: 120,
formatter: (row) => `¥${row.mySalesAmount.toFixed(2)}`
},
{
prop: 'sort',
label: '排序',
width: 80
},
{
prop: 'category',
label: '所属类别',
width: 120
},
{
prop: 'packageCode',
label: '套餐编码',
width: 120
},
{
prop: 'status',
label: '状态',
width: 80,
formatter: (row) => {
return h(ElTag, { type: getStatusType(row.status) }, () => row.status)
}
},
{
prop: 'operation',
label: '操作',
width: 150,
formatter: (row: any) => {
return h('div', { class: 'operation-buttons' }, [
h(ArtButtonTable, {
text: '编辑',
onClick: () => editPackage(row)
}),
h(ArtButtonTable, {
text: row.status === '上架' ? '下架' : '上架',
onClick: () => (row.status === '上架' ? offlinePackage(row) : onlinePackage(row))
})
])
}
}
])
onMounted(() => {
getPackageList()
})
// 获取套餐列表
const getPackageList = async () => {
loading.value = true
try {
// 模拟API调用
await new Promise((resolve) => setTimeout(resolve, 500))
const startIndex = (pagination.currentPage - 1) * pagination.pageSize
const endIndex = startIndex + pagination.pageSize
const paginatedData = mockData.slice(startIndex, endIndex)
tableData.value = paginatedData
pagination.total = mockData.length
loading.value = false
} catch (error) {
console.error('获取套餐列表失败:', error)
loading.value = false
}
}
const handleRefresh = () => {
getPackageList()
}
// 处理表格行选择变化
const handleSelectionChange = (selection: any[]) => {
selectedRows.value = selection
}
// 处理表格分页变化
const handleSizeChange = (newPageSize: number) => {
pagination.pageSize = newPageSize
getPackageList()
}
const handleCurrentChange = (newCurrentPage: number) => {
pagination.currentPage = newCurrentPage
getPackageList()
}
</script>
<style lang="scss" scoped>
.card-template-page {
// 可以添加特定样式
}
</style>

View File

@@ -0,0 +1,462 @@
<template>
<ArtTableFullScreen>
<div class="package-series-page" id="table-full-screen">
<!-- 搜索栏 -->
<ArtSearchBar
v-model:filter="formFilters"
:items="formItems"
@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="handleSearch">搜索</ElButton>
<ElButton type="success" @click="showAddDialog">新增</ElButton>
</template>
</ArtTableHeader>
<!-- 表格 -->
<ArtTable
ref="tableRef"
row-key="id"
:loading="loading"
:data="tableData"
:currentPage="pagination.currentPage"
:pageSize="pagination.pageSize"
:total="pagination.total"
:marginTop="10"
@selection-change="handleSelectionChange"
@size-change="handleSizeChange"
@current-change="handleCurrentChange"
>
<template #default>
<ElTableColumn v-for="col in columns" :key="col.prop || col.type" v-bind="col" />
</template>
</ArtTable>
<!-- 新增套餐系列对话框 -->
<ElDialog
v-model="addDialogVisible"
title="新增套餐系列"
width="500px"
align-center
:close-on-click-modal="false"
>
<ElForm ref="addFormRef" :model="addFormData" :rules="addRules" label-width="120px">
<ElFormItem label="系列名称" prop="seriesName">
<ElInput v-model="addFormData.seriesName" placeholder="请输入系列名称" clearable />
</ElFormItem>
<ElFormItem label="包含套餐" prop="packageNames">
<ElSelect
v-model="addFormData.packageNames"
placeholder="请选择要包含的套餐"
style="width: 100%"
multiple
clearable
>
<ElOption label="随意联畅玩年卡套餐" value="changwan_yearly" />
<ElOption label="随意联畅玩月卡套餐" value="changwan_monthly" />
<ElOption label="如意包年3G流量包" value="ruyi_3g" />
<ElOption label="如意包月流量包" value="ruyi_monthly" />
<ElOption label="Y-NB专享套餐" value="nb_special" />
<ElOption label="NB-IoT基础套餐" value="nb_basic" />
<ElOption label="100G全国流量月卡套餐" value="big_data_100g" />
<ElOption label="200G超值流量包" value="big_data_200g" />
<ElOption label="广电飞悦卡无预存50G" value="gdtv_50g" />
<ElOption label="广电天翼卡" value="gdtv_tianyi" />
</ElSelect>
</ElFormItem>
<ElFormItem label="系列描述" prop="description">
<ElInput
v-model="addFormData.description"
type="textarea"
placeholder="请输入系列描述(可选)"
:rows="3"
maxlength="200"
show-word-limit
/>
</ElFormItem>
</ElForm>
<template #footer>
<div class="dialog-footer">
<ElButton @click="addDialogVisible = false">取消</ElButton>
<ElButton type="primary" @click="handleAddSubmit" :loading="addLoading">
确认新增
</ElButton>
</div>
</template>
</ElDialog>
</ElCard>
</div>
</ArtTableFullScreen>
</template>
<script setup lang="ts">
import { h } from 'vue'
import { ElTag, ElMessage, ElMessageBox } from 'element-plus'
import type { FormInstance, FormRules } from 'element-plus'
import { useCheckedColumns } from '@/composables/useCheckedColumns'
import ArtButtonTable from '@/components/core/forms/ArtButtonTable.vue'
import { SearchChangeParams, SearchFormItem } from '@/types'
defineOptions({ name: 'PackageSeries' })
const addDialogVisible = ref(false)
const loading = ref(false)
const addLoading = ref(false)
// 定义表单搜索初始值
const initialSearchState = {
seriesName: ''
}
// 响应式表单数据
const formFilters = reactive({ ...initialSearchState })
const pagination = reactive({
currentPage: 1,
pageSize: 20,
total: 0
})
// 表格数据
const tableData = ref<any[]>([])
// 表格实例引用
const tableRef = ref()
// 选中的行数据
const selectedRows = ref<any[]>([])
// 新增表单实例
const addFormRef = ref<FormInstance>()
// 新增表单数据
const addFormData = reactive({
seriesName: '',
packageNames: [] as string[],
description: ''
})
// 模拟数据
const mockData = [
{
id: 1,
seriesName: '畅玩系列',
operator: '张若暄',
operationTime: '2025-11-08 10:30:00',
status: '启用'
},
{
id: 2,
seriesName: '如意系列',
operator: '孔丽娟',
operationTime: '2025-11-07 14:15:00',
status: '启用'
},
{
id: 3,
seriesName: 'NB专享',
operator: '李佳音',
operationTime: '2025-11-06 09:45:00',
status: '禁用'
},
{
id: 4,
seriesName: '大流量系列',
operator: '赵强',
operationTime: '2025-11-05 16:20:00',
status: '启用'
},
{
id: 5,
seriesName: '广电系列',
operator: '张若暄',
operationTime: '2025-11-04 11:30:00',
status: '禁用'
}
]
// 重置表单
const handleReset = () => {
Object.assign(formFilters, { ...initialSearchState })
pagination.currentPage = 1
getPackageSeriesList()
}
// 搜索处理
const handleSearch = () => {
console.log('搜索参数:', formFilters)
pagination.currentPage = 1
getPackageSeriesList()
}
// 表单项变更处理
const handleFormChange = (params: SearchChangeParams): void => {
console.log('表单项变更:', params)
}
// 表单配置项
const formItems: SearchFormItem[] = [
{
label: '系列名称',
prop: 'seriesName',
type: 'input',
config: {
clearable: true,
placeholder: '请输入系列名称'
},
onChange: handleFormChange
}
]
// 列配置
const columnOptions = [
{ label: '勾选', type: 'selection' },
{ label: '系列名称', prop: 'seriesName' },
{ label: '操作人', prop: 'operator' },
{ label: '操作时间', prop: 'operationTime' },
{ label: '状态', prop: 'status' },
{ label: '操作', prop: 'operation' }
]
// 获取状态标签类型
const getStatusType = (status: string) => {
switch (status) {
case '启用':
return 'success'
case '禁用':
return 'danger'
default:
return 'info'
}
}
// 显示新增对话框
const showAddDialog = () => {
addDialogVisible.value = true
// 重置表单
if (addFormRef.value) {
addFormRef.value.resetFields()
}
addFormData.seriesName = ''
addFormData.packageNames = []
addFormData.description = ''
}
// 启用系列
const enableSeries = (row: any) => {
ElMessageBox.confirm(`确定要启用套餐系列"${row.seriesName}"吗?`, '启用确认', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'info'
})
.then(() => {
ElMessage.success('启用成功')
getPackageSeriesList()
})
.catch(() => {
ElMessage.info('已取消启用')
})
}
// 禁用系列
const disableSeries = (row: any) => {
ElMessageBox.confirm(`确定要禁用套餐系列"${row.seriesName}"吗?`, '禁用确认', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
})
.then(() => {
ElMessage.success('禁用成功')
getPackageSeriesList()
})
.catch(() => {
ElMessage.info('已取消禁用')
})
}
// 删除系列
const deleteSeries = (row: any) => {
ElMessageBox.confirm(
`确定要删除套餐系列"${row.seriesName}"吗?删除后将无法恢复。`,
'删除确认',
{
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'error'
}
)
.then(() => {
ElMessage.success('删除成功')
getPackageSeriesList()
})
.catch(() => {
ElMessage.info('已取消删除')
})
}
// 动态列配置
const { columnChecks, columns } = useCheckedColumns(() => [
{ type: 'selection' },
{
prop: 'seriesName',
label: '系列名称',
minWidth: 180
},
{
prop: 'operator',
label: '操作人',
width: 120
},
{
prop: 'operationTime',
label: '操作时间',
width: 160
},
{
prop: 'status',
label: '状态',
width: 100,
formatter: (row) => {
return h(ElTag, { type: getStatusType(row.status) }, () => row.status)
}
},
{
prop: 'operation',
label: '操作',
width: 180,
formatter: (row: any) => {
const buttons = []
if (row.status === '启用') {
buttons.push(
h(ArtButtonTable, {
text: '禁用',
onClick: () => disableSeries(row)
})
)
} else {
buttons.push(
h(ArtButtonTable, {
text: '启用',
onClick: () => enableSeries(row)
})
)
}
buttons.push(
h(ArtButtonTable, {
text: '删除',
onClick: () => deleteSeries(row)
})
)
return h('div', { class: 'operation-buttons' }, buttons)
}
}
])
onMounted(() => {
getPackageSeriesList()
})
// 获取套餐系列列表
const getPackageSeriesList = async () => {
loading.value = true
try {
// 模拟API调用
await new Promise((resolve) => setTimeout(resolve, 500))
const startIndex = (pagination.currentPage - 1) * pagination.pageSize
const endIndex = startIndex + pagination.pageSize
const paginatedData = mockData.slice(startIndex, endIndex)
tableData.value = paginatedData
pagination.total = mockData.length
loading.value = false
} catch (error) {
console.error('获取套餐系列列表失败:', error)
loading.value = false
}
}
const handleRefresh = () => {
getPackageSeriesList()
}
// 处理表格行选择变化
const handleSelectionChange = (selection: any[]) => {
selectedRows.value = selection
}
// 处理表格分页变化
const handleSizeChange = (newPageSize: number) => {
pagination.pageSize = newPageSize
getPackageSeriesList()
}
const handleCurrentChange = (newCurrentPage: number) => {
pagination.currentPage = newCurrentPage
getPackageSeriesList()
}
// 新增表单验证规则
const addRules = reactive<FormRules>({
seriesName: [
{ required: true, message: '请输入系列名称', trigger: 'blur' },
{ min: 2, max: 20, message: '系列名称长度在 2 到 20 个字符', trigger: 'blur' }
],
packageNames: [{ required: true, message: '请选择要包含的套餐', trigger: 'change' }]
})
// 提交新增
const handleAddSubmit = async () => {
if (!addFormRef.value) return
await addFormRef.value.validate((valid) => {
if (valid) {
addLoading.value = true
// 模拟新增过程
setTimeout(() => {
ElMessage.success(
`新增套餐系列成功!系列名称:${addFormData.seriesName},包含套餐:${addFormData.packageNames.length}`
)
addDialogVisible.value = false
addLoading.value = false
getPackageSeriesList()
}, 2000)
}
})
}
</script>
<style lang="scss" scoped>
.package-series-page {
// 可以添加特定样式
}
:deep(.operation-buttons) {
display: flex;
gap: 8px;
}
.dialog-footer {
text-align: right;
}
:deep(.el-form-item) {
margin-bottom: 20px;
}
</style>