Files
one-pipe-system/src/views/package-management/package-batch/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

524 lines
14 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-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>