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,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>