Files
one-pipe-system/src/views/card-management/package-gift/index.vue
sexygoat 222e5bb11a Initial commit: One Pipe System
完整的管理系统,包含账户管理、卡片管理、套餐管理、财务管理等功能模块。

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

Co-Authored-By: Claude <noreply@anthropic.com>
2026-01-22 16:35:33 +08:00

690 lines
18 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<template>
<ArtTableFullScreen>
<div class="package-gift-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="showBatchImportDialog">批量导入</ElButton>
<ElButton type="danger" @click="batchDelete">批量删除</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="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="上传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, ElMessageBox, 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: 'PackageGift' })
const importDialogVisible = ref(false)
const loading = ref(false)
const importLoading = ref(false)
// 定义表单搜索初始值
const initialSearchState = {
iccid: '',
accessNumber: '',
cardCompany: '',
isReceived: '',
endDateRange: ''
}
// 响应式表单数据
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({
excelFile: null as File | null,
remark: ''
})
// 模拟数据
const mockData = [
{
id: 1,
iccid: '89860621370079892035',
accessNumber: '1440012345678',
giftPackage: '随意联畅玩年卡套餐12个月',
cardCompany: '联通2',
isReceived: '已领取',
operator: '张若暄',
operationTime: '2025-11-08 10:30:00',
receiveTime: '2025-11-08 14:20:00',
importStatus: '导入成功',
failureReason: ''
},
{
id: 2,
iccid: '89860621370079892036',
accessNumber: '1440012345679',
giftPackage: 'Y-NB专享套餐',
cardCompany: 'SXKJ-NB',
isReceived: '未领取',
operator: '孔丽娟',
operationTime: '2025-11-07 14:15:00',
receiveTime: '',
importStatus: '导入成功',
failureReason: ''
},
{
id: 3,
iccid: '89860621370079892037',
accessNumber: '1440012345680',
giftPackage: '如意包年3G流量包',
cardCompany: '联通36',
isReceived: '已过期',
operator: '李佳音',
operationTime: '2025-11-06 09:45:00',
receiveTime: '',
importStatus: '导入成功',
failureReason: ''
},
{
id: 4,
iccid: '89860621370079892038',
accessNumber: '1440012345681',
giftPackage: '100G全国流量月卡套餐',
cardCompany: '联通1-1',
isReceived: '未领取',
operator: '赵强',
operationTime: '2025-11-05 16:20:00',
receiveTime: '',
importStatus: '导入失败',
failureReason: 'ICCID格式错误'
},
{
id: 5,
iccid: '89860621370079892039',
accessNumber: '1440012345682',
giftPackage: '广电飞悦卡无预存50G30天',
cardCompany: '广电4',
isReceived: '已领取',
operator: '张若暄',
operationTime: '2025-11-04 11:30:00',
receiveTime: '2025-11-05 08:15:00',
importStatus: '导入成功',
failureReason: ''
}
]
// 重置表单
const handleReset = () => {
Object.assign(formFilters, { ...initialSearchState })
pagination.currentPage = 1
getPackageGiftList()
}
// 搜索处理
const handleSearch = () => {
console.log('搜索参数:', formFilters)
pagination.currentPage = 1
getPackageGiftList()
}
// 表单项变更处理
const handleFormChange = (params: SearchChangeParams): void => {
console.log('表单项变更:', params)
}
// 表单配置项
const formItems: SearchFormItem[] = [
{
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: 'cardCompany',
type: 'select',
config: {
clearable: true,
placeholder: '全部'
},
options: () => [
{ label: '联通2', value: 'unicom2' },
{ label: '联通36', value: 'unicom36' },
{ label: 'SXKJ-NB', value: 'sxkj_nb' },
{ label: '联通1-1', value: 'unicom1_1' },
{ label: '联通8', value: 'unicom8' },
{ label: '移动21', value: 'mobile21' },
{ label: '广电4', value: 'gdtv4' },
{ label: '电信9', value: 'telecom9' }
],
onChange: handleFormChange
},
{
label: '是否领取',
prop: 'isReceived',
type: 'select',
config: {
clearable: true,
placeholder: '全部'
},
options: () => [
{ label: '已领取', value: 'received' },
{ label: '未领取', value: 'not_received' },
{ label: '已过期', value: 'expired' }
],
onChange: handleFormChange
},
{
label: '结束时间',
prop: 'endDateRange',
type: 'daterange',
config: {
type: 'daterange',
startPlaceholder: '开始时间',
endPlaceholder: '结束时间'
},
onChange: handleFormChange
}
]
// 列配置
const columnOptions = [
{ label: '勾选', type: 'selection' },
{ label: 'ICCID', prop: 'iccid' },
{ label: '接入号码', prop: 'accessNumber' },
{ label: '赠送套餐', prop: 'giftPackage' },
{ label: '开卡公司', prop: 'cardCompany' },
{ label: '是否领取', prop: 'isReceived' },
{ label: '操作人', prop: 'operator' },
{ label: '操作时间', prop: 'operationTime' },
{ label: '领取时间', prop: 'receiveTime' },
{ label: '导入状态', prop: 'importStatus' },
{ label: '失败原因', prop: 'failureReason' },
{ label: '操作', prop: 'operation' }
]
// 获取是否领取标签类型
const getReceiveStatusType = (status: string) => {
switch (status) {
case '已领取':
return 'success'
case '未领取':
return 'warning'
case '已过期':
return 'danger'
default:
return 'info'
}
}
// 获取导入状态标签类型
const getImportStatusType = (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 batchDelete = () => {
if (selectedRows.value.length === 0) {
ElMessage.warning('请先选择要删除的数据')
return
}
ElMessageBox.confirm(
`确定要删除选中的 ${selectedRows.value.length} 条套餐赠送记录吗?`,
'批量删除确认',
{
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}
)
.then(() => {
ElMessage.success(`批量删除 ${selectedRows.value.length} 条记录成功`)
getPackageGiftList()
})
.catch(() => {
ElMessage.info('已取消删除')
})
}
// 显示导入对话框
const showBatchImportDialog = () => {
importDialogVisible.value = true
// 重置表单
if (importFormRef.value) {
importFormRef.value.resetFields()
}
importFormData.excelFile = null
importFormData.remark = ''
}
// 下载模板
const downloadTemplate = () => {
ElMessage.success('正在下载套餐赠送导入模板...')
// 这里可以实现实际的模板下载功能
}
// 查看详情
const viewDetails = (row: any) => {
ElMessage.info(`查看套餐赠送详情: ${row.iccid}`)
}
// 编辑记录
const editRecord = (row: any) => {
ElMessage.info(`编辑套餐赠送记录: ${row.iccid}`)
}
// 删除记录
const deleteRecord = (row: any) => {
ElMessageBox.confirm(`确定要删除该套餐赠送记录吗?`, '删除确认', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
})
.then(() => {
ElMessage.success('删除成功')
getPackageGiftList()
})
.catch(() => {
ElMessage.info('已取消删除')
})
}
// 手动发放
const manualGrant = (row: any) => {
if (row.isReceived === '已领取') {
ElMessage.warning('该套餐已被领取')
return
}
ElMessageBox.confirm(`确定要手动发放该套餐吗?`, '发放确认', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'info'
})
.then(() => {
ElMessage.success('套餐发放成功')
getPackageGiftList()
})
.catch(() => {
ElMessage.info('已取消发放')
})
}
// 动态列配置
const { columnChecks, columns } = useCheckedColumns(() => [
{ type: 'selection' },
{
prop: 'iccid',
label: 'ICCID',
minWidth: 180
},
{
prop: 'accessNumber',
label: '接入号码',
width: 140
},
{
prop: 'giftPackage',
label: '赠送套餐',
minWidth: 200
},
{
prop: 'cardCompany',
label: '开卡公司',
width: 120
},
{
prop: 'isReceived',
label: '是否领取',
width: 100,
formatter: (row) => {
return h(ElTag, { type: getReceiveStatusType(row.isReceived) }, () => row.isReceived)
}
},
{
prop: 'operator',
label: '操作人',
width: 100
},
{
prop: 'operationTime',
label: '操作时间',
width: 160
},
{
prop: 'receiveTime',
label: '领取时间',
width: 160,
formatter: (row) => row.receiveTime || '未领取'
},
{
prop: 'importStatus',
label: '导入状态',
width: 100,
formatter: (row) => {
return h(ElTag, { type: getImportStatusType(row.importStatus) }, () => row.importStatus)
}
},
{
prop: 'failureReason',
label: '失败原因',
width: 140,
formatter: (row) => row.failureReason || '-'
},
{
prop: 'operation',
label: '操作',
width: 280,
formatter: (row: any) => {
return h('div', { class: 'operation-buttons' }, [
h(ArtButtonTable, {
text: '查看',
onClick: () => viewDetails(row)
}),
h(ArtButtonTable, {
text: '发放',
disabled: row.isReceived === '已领取',
onClick: () => manualGrant(row)
}),
h(ArtButtonTable, {
text: '编辑',
onClick: () => editRecord(row)
}),
h(ArtButtonTable, {
text: '删除',
onClick: () => deleteRecord(row)
})
])
}
}
])
onMounted(() => {
getPackageGiftList()
})
// 获取套餐赠送列表
const getPackageGiftList = 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 = () => {
getPackageGiftList()
}
// 处理表格行选择变化
const handleSelectionChange = (selection: any[]) => {
selectedRows.value = selection
}
// 处理表格分页变化
const handleSizeChange = (newPageSize: number) => {
pagination.pageSize = newPageSize
getPackageGiftList()
}
const handleCurrentChange = (newCurrentPage: number) => {
pagination.currentPage = newCurrentPage
getPackageGiftList()
}
// 文件上传限制
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>({
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('套餐赠送导入成功!')
importDialogVisible.value = false
importLoading.value = false
getPackageGiftList()
}, 2000)
}
})
}
</script>
<style lang="scss" scoped>
.package-gift-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>