fetch(add): 订单管理-企业设备
All checks were successful
构建并部署前端到测试环境 / build-and-deploy (push) Successful in 3m30s

This commit is contained in:
sexygoat
2026-01-29 15:43:45 +08:00
parent 1812b7a6c4
commit 841cf0442b
58 changed files with 8948 additions and 1164 deletions

View File

@@ -3,8 +3,9 @@
<div class="package-series-page" id="table-full-screen">
<!-- 搜索栏 -->
<ArtSearchBar
v-model:filter="formFilters"
:items="formItems"
v-model:filter="searchForm"
:items="searchFormItems"
:show-expand="false"
@reset="handleReset"
@search="handleSearch"
></ArtSearchBar>
@@ -17,8 +18,7 @@
@refresh="handleRefresh"
>
<template #left>
<ElButton type="primary" @click="handleSearch">搜索</ElButton>
<ElButton type="success" @click="showAddDialog">新增</ElButton>
<ElButton type="primary" @click="showDialog('add')">新增套餐系列</ElButton>
</template>
</ArtTableHeader>
@@ -27,12 +27,11 @@
ref="tableRef"
row-key="id"
:loading="loading"
:data="tableData"
:currentPage="pagination.currentPage"
:pageSize="pagination.pageSize"
:data="seriesList"
:currentPage="pagination.page"
:pageSize="pagination.page_size"
:total="pagination.total"
:marginTop="10"
@selection-change="handleSelectionChange"
@size-change="handleSizeChange"
@current-change="handleCurrentChange"
>
@@ -41,57 +40,41 @@
</template>
</ArtTable>
<!-- 新增套餐系列对话框 -->
<!-- 新增/编辑对话框 -->
<ElDialog
v-model="addDialogVisible"
title="新增套餐系列"
v-model="dialogVisible"
:title="dialogType === 'add' ? '新增套餐系列' : '编辑套餐系列'"
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
<ElForm ref="formRef" :model="form" :rules="rules" label-width="120px">
<ElFormItem label="系列编码" prop="series_code">
<ElInput
v-model="form.series_code"
placeholder="请输入系列编码"
:disabled="dialogType === 'edit'"
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="series_name">
<ElInput v-model="form.series_name" placeholder="请输入系列名称" clearable />
</ElFormItem>
<ElFormItem label="系列描述" prop="description">
<ElInput
v-model="addFormData.description"
v-model="form.description"
type="textarea"
placeholder="请输入系列描述(可选)"
:rows="3"
maxlength="200"
placeholder="请输入系列描述(可选)"
maxlength="500"
show-word-limit
/>
</ElFormItem>
</ElForm>
<template #footer>
<div class="dialog-footer">
<ElButton @click="addDialogVisible = false">取消</ElButton>
<ElButton type="primary" @click="handleAddSubmit" :loading="addLoading">
确认新增
<ElButton @click="dialogVisible = false">取消</ElButton>
<ElButton type="primary" @click="handleSubmit(formRef)" :loading="submitLoading">
提交
</ElButton>
</div>
</template>
@@ -103,343 +86,328 @@
<script setup lang="ts">
import { h } from 'vue'
import { ElTag, ElMessage, ElMessageBox } from 'element-plus'
import { PackageSeriesService } from '@/api/modules'
import { ElMessage, ElMessageBox, ElTag, ElSwitch } from 'element-plus'
import type { FormInstance, FormRules } from 'element-plus'
import type { PackageSeriesResponse } from '@/types/api'
import type { SearchFormItem } from '@/types'
import { useCheckedColumns } from '@/composables/useCheckedColumns'
import ArtButtonTable from '@/components/core/forms/ArtButtonTable.vue'
import { SearchChangeParams, SearchFormItem } from '@/types'
import { formatDateTime } from '@/utils/business/format'
import {
CommonStatus,
getStatusText,
frontendStatusToApi,
apiStatusToFrontend
} from '@/config/constants'
defineOptions({ name: 'PackageSeries' })
const addDialogVisible = ref(false)
const dialogVisible = 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 submitLoading = ref(false)
const tableRef = ref()
const formRef = ref<FormInstance>()
// 选中的行数据
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 initialSearchState = {
series_name: '',
status: undefined as number | undefined
}
// 搜索处理
const handleSearch = () => {
console.log('搜索参数:', formFilters)
pagination.currentPage = 1
getPackageSeriesList()
}
// 搜索表单
const searchForm = reactive({ ...initialSearchState })
// 表单项变更处理
const handleFormChange = (params: SearchChangeParams): void => {
console.log('表单项变更:', params)
}
// 表单配置项
const formItems: SearchFormItem[] = [
// 搜索表单配置
const searchFormItems: SearchFormItem[] = [
{
label: '系列名称',
prop: 'seriesName',
prop: 'series_name',
type: 'input',
config: {
clearable: true,
placeholder: '请输入系列名称'
}
},
{
label: '状态',
prop: 'status',
type: 'select',
config: {
clearable: true,
placeholder: '请选择状态'
},
onChange: handleFormChange
options: () => [
{ label: '启用', value: 1 },
{ label: '禁用', value: 2 }
]
}
]
// 分页
const pagination = reactive({
page: 1,
page_size: 20,
total: 0
})
// 列配置
const columnOptions = [
{ label: '勾选', type: 'selection' },
{ label: '系列名称', prop: 'seriesName' },
{ label: '操作人', prop: 'operator' },
{ label: '操作时间', prop: 'operationTime' },
{ label: 'ID', prop: 'id' },
{ label: '系列编码', prop: 'series_code' },
{ label: '系列名称', prop: 'series_name' },
{ label: '描述', prop: 'description' },
{ label: '状态', prop: 'status' },
{ label: '创建时间', prop: 'created_at' },
{ label: '更新时间', prop: 'updated_at' },
{ label: '操作', prop: 'operation' }
]
// 获取状态标签类型
const getStatusType = (status: string) => {
switch (status) {
case '启用':
return 'success'
case '禁用':
return 'danger'
default:
return 'info'
}
}
// 表单验证规则
const rules = reactive<FormRules>({
series_code: [
{ required: true, message: '请输入系列编码', trigger: 'blur' },
{ min: 1, max: 100, message: '长度在 1 到 100 个字符', trigger: 'blur' }
],
series_name: [
{ required: true, message: '请输入系列名称', trigger: 'blur' },
{ min: 1, max: 255, message: '长度在 1 到 255 个字符', trigger: 'blur' }
],
description: [{ max: 500, message: '描述不能超过 500 个字符', trigger: 'blur' }]
})
// 显示新增对话框
const showAddDialog = () => {
addDialogVisible.value = true
// 重置表单
if (addFormRef.value) {
addFormRef.value.resetFields()
}
addFormData.seriesName = ''
addFormData.packageNames = []
addFormData.description = ''
}
// 表单数据
const form = reactive<any>({
id: 0,
series_code: '',
series_name: '',
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 seriesList = ref<PackageSeriesResponse[]>([])
const dialogType = ref('add')
// 动态列配置
const { columnChecks, columns } = useCheckedColumns(() => [
{ type: 'selection' },
{
prop: 'seriesName',
prop: 'id',
label: 'ID',
width: 80
},
{
prop: 'series_code',
label: '系列编码',
minWidth: 150
},
{
prop: 'series_name',
label: '系列名称',
minWidth: 180
minWidth: 150
},
{
prop: 'operator',
label: '操作人',
width: 120
},
{
prop: 'operationTime',
label: '操作时间',
width: 160
prop: 'description',
label: '描述',
minWidth: 200
},
{
prop: 'status',
label: '状态',
width: 100,
formatter: (row) => {
return h(ElTag, { type: getStatusType(row.status) }, () => row.status)
formatter: (row: PackageSeriesResponse) => {
const frontendStatus = apiStatusToFrontend(row.status)
return h(ElSwitch, {
modelValue: frontendStatus,
activeValue: CommonStatus.ENABLED,
inactiveValue: CommonStatus.DISABLED,
activeText: getStatusText(CommonStatus.ENABLED),
inactiveText: getStatusText(CommonStatus.DISABLED),
inlinePrompt: true,
'onUpdate:modelValue': (val: string | number | boolean) =>
handleStatusChange(row, val as number)
})
}
},
{
prop: 'created_at',
label: '创建时间',
width: 180,
formatter: (row: PackageSeriesResponse) => formatDateTime(row.created_at)
},
{
prop: 'updated_at',
label: '更新时间',
width: 180,
formatter: (row: PackageSeriesResponse) => formatDateTime(row.updated_at)
},
{
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(
width: 150,
fixed: 'right',
formatter: (row: PackageSeriesResponse) => {
return h('div', { style: 'display: flex; gap: 8px;' }, [
h(ArtButtonTable, {
text: '删除',
type: 'edit',
onClick: () => showDialog('edit', row)
}),
h(ArtButtonTable, {
type: 'delete',
onClick: () => deleteSeries(row)
})
)
return h('div', { class: 'operation-buttons' }, buttons)
])
}
}
])
onMounted(() => {
getPackageSeriesList()
getTableData()
})
// 获取套餐系列列表
const getPackageSeriesList = async () => {
const getTableData = 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
const params = {
page: pagination.page,
page_size: pagination.page_size,
series_name: searchForm.series_name || undefined,
status: searchForm.status || undefined
}
const res = await PackageSeriesService.getPackageSeries(params)
if (res.code === 0) {
seriesList.value = res.data.items || []
pagination.total = res.data.total || 0
}
} catch (error) {
console.error('获取套餐系列列表失败:', error)
console.error(error)
ElMessage.error('获取套餐系列列表失败')
} finally {
loading.value = false
}
}
const handleRefresh = () => {
getPackageSeriesList()
// 重置搜索
const handleReset = () => {
Object.assign(searchForm, { ...initialSearchState })
pagination.page = 1
getTableData()
}
// 处理表格行选择变化
const handleSelectionChange = (selection: any[]) => {
selectedRows.value = selection
// 搜索
const handleSearch = () => {
pagination.page = 1
getTableData()
}
// 刷新表格
const handleRefresh = () => {
getTableData()
}
// 处理表格分页变化
const handleSizeChange = (newPageSize: number) => {
pagination.pageSize = newPageSize
getPackageSeriesList()
pagination.page_size = newPageSize
getTableData()
}
const handleCurrentChange = (newCurrentPage: number) => {
pagination.currentPage = newCurrentPage
getPackageSeriesList()
pagination.page = newCurrentPage
getTableData()
}
// 新增表单验证规则
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 showDialog = (type: string, row?: PackageSeriesResponse) => {
dialogVisible.value = true
dialogType.value = type
// 提交新增
const handleAddSubmit = async () => {
if (!addFormRef.value) return
if (type === 'edit' && row) {
form.id = row.id
form.series_code = row.series_code
form.series_name = row.series_name
form.description = row.description || ''
} else {
form.id = 0
form.series_code = ''
form.series_name = ''
form.description = ''
}
await addFormRef.value.validate((valid) => {
// 重置表单验证状态
nextTick(() => {
formRef.value?.clearValidate()
})
}
// 删除套餐系列
const deleteSeries = (row: PackageSeriesResponse) => {
ElMessageBox.confirm(`确定删除套餐系列 ${row.series_name} 吗?`, '删除确认', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'error'
})
.then(async () => {
try {
await PackageSeriesService.deletePackageSeries(row.id)
ElMessage.success('删除成功')
await getTableData()
} catch (error) {
console.error(error)
}
})
.catch(() => {
// 用户取消删除
})
}
// 提交表单
const handleSubmit = async (formEl: FormInstance | undefined) => {
if (!formEl) return
await formEl.validate(async (valid) => {
if (valid) {
addLoading.value = true
submitLoading.value = true
try {
const data = {
series_code: form.series_code,
series_name: form.series_name,
description: form.description || undefined
}
// 模拟新增过程
setTimeout(() => {
ElMessage.success(
`新增套餐系列成功!系列名称:${addFormData.seriesName},包含套餐:${addFormData.packageNames.length}`
)
addDialogVisible.value = false
addLoading.value = false
getPackageSeriesList()
}, 2000)
if (dialogType.value === 'add') {
await PackageSeriesService.createPackageSeries(data)
ElMessage.success('新增成功')
} else {
await PackageSeriesService.updatePackageSeries(form.id, data)
ElMessage.success('修改成功')
}
dialogVisible.value = false
formEl.resetFields()
await getTableData()
} catch (error) {
console.error(error)
} finally {
submitLoading.value = false
}
}
})
}
// 状态切换
const handleStatusChange = async (row: PackageSeriesResponse, newFrontendStatus: number) => {
const oldStatus = row.status
const newApiStatus = frontendStatusToApi(newFrontendStatus)
// 先更新UI将后端状态转换
row.status = newApiStatus
try {
await PackageSeriesService.updatePackageSeriesStatus(row.id, newApiStatus)
ElMessage.success('状态切换成功')
} catch (error) {
// 切换失败,恢复原状态
row.status = oldStatus
console.error(error)
}
}
</script>
<style lang="scss" scoped>
@@ -447,16 +415,7 @@
// 可以添加特定样式
}
:deep(.operation-buttons) {
display: flex;
gap: 8px;
}
.dialog-footer {
text-align: right;
}
:deep(.el-form-item) {
margin-bottom: 20px;
}
</style>