Files
one-pipe-system/src/views/order-management/order-list/index.vue
sexygoat f06d8c9133
All checks were successful
构建并部署前端到测试环境 / build-and-deploy (push) Successful in 5m7s
新增
2026-03-19 18:32:02 +08:00

1181 lines
38 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="order-list-page" id="table-full-screen">
<!-- 搜索栏 -->
<ArtSearchBar
v-model:filter="searchForm"
:items="searchFormItems"
:show-expand="false"
@reset="handleReset"
@search="handleSearch"
></ArtSearchBar>
<ElCard shadow="never" class="art-table-card">
<!-- 表格头部 -->
<ArtTableHeader
:columnList="columnOptions"
v-model:columns="columnChecks"
@refresh="handleRefresh"
>
<template #left>
<ElButton @click="showCreateDialog" v-permission="'orders:add'">{{
t('orderManagement.createOrder')
}}</ElButton>
</template>
</ArtTableHeader>
<!-- 表格 -->
<ArtTable
ref="tableRef"
row-key="id"
:loading="loading"
:data="orderList"
:currentPage="pagination.page"
:pageSize="pagination.page_size"
:total="pagination.total"
:marginTop="10"
:row-class-name="getRowClassName"
@size-change="handleSizeChange"
@current-change="handleCurrentChange"
@row-contextmenu="handleRowContextMenu"
@cell-mouse-enter="handleCellMouseEnter"
@cell-mouse-leave="handleCellMouseLeave"
>
<template #default>
<ElTableColumn v-for="col in columns" :key="col.prop || col.type" v-bind="col" />
</template>
</ArtTable>
<!-- 鼠标悬浮提示 -->
<TableContextMenuHint :visible="showContextMenuHint" :position="hintPosition" />
<!-- 右键菜单 -->
<ArtMenuRight
ref="contextMenuRef"
:menu-items="contextMenuItems"
:menu-width="120"
@select="handleContextMenuSelect"
/>
<!-- 创建订单对话框 -->
<ElDialog
v-model="createDialogVisible"
:title="t('orderManagement.createOrder')"
width="600px"
@closed="handleCreateDialogClosed"
>
<ElForm ref="createFormRef" :model="createForm" :rules="createRules" label-width="120px">
<ElFormItem :label="t('orderManagement.table.orderType')" prop="order_type">
<ElSelect
v-model="createForm.order_type"
:placeholder="t('orderManagement.searchForm.orderTypePlaceholder')"
style="width: 100%"
>
<ElOption :label="t('orderManagement.orderType.singleCard')" value="single_card" />
<ElOption :label="t('orderManagement.orderType.device')" value="device" />
</ElSelect>
</ElFormItem>
<ElFormItem
v-if="createForm.order_type === 'single_card'"
:label="t('orderManagement.createForm.iotCardId')"
prop="iot_card_id"
>
<ElSelect
v-model="createForm.iot_card_id"
filterable
remote
reserve-keyword
:placeholder="t('orderManagement.createForm.iotCardIdPlaceholder')"
:remote-method="searchIotCards"
:loading="cardSearchLoading"
style="width: 100%"
clearable
>
<ElOption
v-for="card in iotCardOptions"
:key="card.id"
:label="`${card.iccid} (${card.msisdn || '无接入号'})`"
:value="card.id"
>
<div style="display: flex; justify-content: space-between">
<span>{{ card.iccid }}</span>
<span style="color: var(--el-text-color-secondary); font-size: 12px">
{{ card.msisdn || '无接入号' }}
</span>
</div>
</ElOption>
</ElSelect>
</ElFormItem>
<ElFormItem
v-if="createForm.order_type === 'device'"
:label="t('orderManagement.createForm.deviceId')"
prop="device_id"
>
<ElSelect
v-model="createForm.device_id"
filterable
remote
reserve-keyword
:placeholder="t('orderManagement.createForm.deviceIdPlaceholder')"
:remote-method="searchDevices"
:loading="deviceSearchLoading"
style="width: 100%"
clearable
>
<ElOption
v-for="device in deviceOptions"
:key="device.id"
:label="`${device.virtual_no} (${device.device_name})`"
:value="device.id"
>
<div style="display: flex; justify-content: space-between">
<span>{{ device.virtual_no }}</span>
<span style="color: var(--el-text-color-secondary); font-size: 12px">
{{ device.device_name }}
</span>
</div>
</ElOption>
</ElSelect>
</ElFormItem>
<ElFormItem :label="t('orderManagement.createForm.packageIds')" prop="package_ids">
<ElSelect
v-model="createForm.package_ids"
:placeholder="t('orderManagement.createForm.packageIdsPlaceholder')"
multiple
filterable
remote
reserve-keyword
:remote-method="handlePackageSearch"
:loading="packageSearchLoading"
:disabled="
(!createForm.iot_card_id && !createForm.device_id) ||
(createForm.order_type === 'single_card' && !createForm.iot_card_id) ||
(createForm.order_type === 'device' && !createForm.device_id)
"
clearable
style="width: 100%"
>
<ElOption
v-for="pkg in packageOptions"
:key="pkg.id"
:label="`${pkg.package_name} (¥${(pkg.cost_price / 100).toFixed(2)})`"
:value="pkg.id"
>
<div style="display: flex; justify-content: space-between">
<span>{{ pkg.package_name }}</span>
<span style="color: var(--el-text-color-secondary); font-size: 12px">
¥{{ (pkg.cost_price / 100).toFixed(2) }}
</span>
</div>
</ElOption>
</ElSelect>
</ElFormItem>
<ElFormItem label="支付方式" prop="payment_method">
<ElSelect
v-model="createForm.payment_method"
placeholder="请选择支付方式"
style="width: 100%"
>
<ElOption label="钱包支付" value="wallet" />
<!-- 只有平台用户(user_type 1:超级管理员, 2:平台用户)才显示线下支付选项 -->
<ElOption
v-if="userStore.info.user_type === 1 || userStore.info.user_type === 2"
label="线下支付"
value="offline"
/>
</ElSelect>
<div style="margin-top: 8px; font-size: 12px; color: var(--el-text-color-secondary)">
<template v-if="createForm.payment_method === 'wallet'">
提示: 使用钱包支付时,订单将直接完成
</template>
<template v-else-if="createForm.payment_method === 'offline'">
提示: 线下支付订单需要手动确认支付
</template>
</div>
</ElFormItem>
</ElForm>
<template #footer>
<div class="dialog-footer">
<ElButton @click="createDialogVisible = false">{{
t('orderManagement.actions.cancel')
}}</ElButton>
<ElButton
type="primary"
@click="handleCreateOrder(createFormRef)"
:loading="createLoading"
>
{{ t('orderManagement.actions.submit') }}
</ElButton>
</div>
</template>
</ElDialog>
<!-- 订单详情对话框 -->
<ElDialog
v-model="detailDialogVisible"
:title="t('orderManagement.orderDetail')"
width="800px"
>
<div v-if="currentOrder" class="order-detail">
<ElDescriptions :column="2" border>
<ElDescriptionsItem :label="t('orderManagement.table.orderNo')">
{{ currentOrder.order_no }}
</ElDescriptionsItem>
<ElDescriptionsItem :label="t('orderManagement.table.orderType')">
{{ getOrderTypeText(currentOrder.order_type) }}
</ElDescriptionsItem>
<ElDescriptionsItem :label="t('orderManagement.table.paymentStatus')">
<ElTag :type="getPaymentStatusType(currentOrder.payment_status)">
{{ currentOrder.payment_status_text }}
</ElTag>
</ElDescriptionsItem>
<ElDescriptionsItem :label="t('orderManagement.table.totalAmount')">
{{ formatCurrency(currentOrder.total_amount) }}
</ElDescriptionsItem>
<ElDescriptionsItem :label="t('orderManagement.table.paymentMethod')">
{{ getPaymentMethodText(currentOrder.payment_method) }}
</ElDescriptionsItem>
<ElDescriptionsItem :label="t('orderManagement.table.buyerType')">
{{ getBuyerTypeText(currentOrder.buyer_type) }}
</ElDescriptionsItem>
<ElDescriptionsItem :label="t('orderManagement.table.commissionStatus')">
{{ getCommissionStatusText(currentOrder.commission_status) }}
</ElDescriptionsItem>
<ElDescriptionsItem :label="t('orderManagement.table.paidAt')">
{{ currentOrder.paid_at ? formatDateTime(currentOrder.paid_at) : '-' }}
</ElDescriptionsItem>
<ElDescriptionsItem :label="t('orderManagement.table.createdAt')">
{{ formatDateTime(currentOrder.created_at) }}
</ElDescriptionsItem>
<ElDescriptionsItem :label="t('orderManagement.table.updatedAt')">
{{ formatDateTime(currentOrder.updated_at) }}
</ElDescriptionsItem>
</ElDescriptions>
<!-- 订单项列表 -->
<div
v-if="currentOrder.items && currentOrder.items.length > 0"
style="margin-top: 20px"
>
<h4>{{ t('orderManagement.orderItems') }}</h4>
<ElTable :data="currentOrder.items" border style="margin-top: 10px">
<ElTableColumn
prop="package_name"
:label="t('orderManagement.items.packageName')"
min-width="150"
/>
<ElTableColumn
prop="quantity"
:label="t('orderManagement.items.quantity')"
width="100"
/>
<ElTableColumn
prop="unit_price"
:label="t('orderManagement.items.unitPrice')"
width="120"
>
<template #default="{ row }">
{{ formatCurrency(row.unit_price) }}
</template>
</ElTableColumn>
<ElTableColumn prop="amount" :label="t('orderManagement.items.amount')" width="120">
<template #default="{ row }">
{{ formatCurrency(row.amount) }}
</template>
</ElTableColumn>
</ElTable>
</div>
</div>
<template #footer>
<div class="dialog-footer">
<ElButton @click="detailDialogVisible = false">{{
t('orderManagement.actions.close')
}}</ElButton>
</div>
</template>
</ElDialog>
</ElCard>
</div>
</ArtTableFullScreen>
</template>
<script setup lang="ts">
import { h } from 'vue'
import { useI18n } from 'vue-i18n'
import { useRouter } from 'vue-router'
import { OrderService, CardService, DeviceService, PackageManageService } from '@/api/modules'
import { ElMessage, ElMessageBox, ElTag } from 'element-plus'
import type { FormInstance, FormRules } from 'element-plus'
import type {
Order,
OrderQueryParams,
CreateOrderRequest,
PaymentStatus,
OrderType,
BuyerType,
OrderPaymentMethod,
OrderCommissionStatus,
StandaloneIotCard,
Device,
PackageResponse,
PurchaseCheckRequest,
PurchaseCheckResponse
} from '@/types/api'
import type { SearchFormItem } from '@/types'
import { useCheckedColumns } from '@/composables/useCheckedColumns'
import { useAuth } from '@/composables/useAuth'
import { useTableContextMenu } from '@/composables/useTableContextMenu'
import { useUserStore } from '@/store/modules/user'
import ArtButtonTable from '@/components/core/forms/ArtButtonTable.vue'
import ArtMenuRight from '@/components/core/others/ArtMenuRight.vue'
import TableContextMenuHint from '@/components/core/others/TableContextMenuHint.vue'
import type { MenuItemType } from '@/components/core/others/ArtMenuRight.vue'
import { formatDateTime } from '@/utils/business/format'
import { RoutesAlias } from '@/router/routesAlias'
defineOptions({ name: 'OrderList' })
const { t } = useI18n()
const router = useRouter()
const { hasAuth } = useAuth()
const userStore = useUserStore()
// 使用表格右键菜单功能
const {
showContextMenuHint,
hintPosition,
getRowClassName,
handleCellMouseEnter,
handleCellMouseLeave
} = useTableContextMenu()
const loading = ref(false)
const createLoading = ref(false)
const tableRef = ref()
const createDialogVisible = ref(false)
const detailDialogVisible = ref(false)
const currentOrder = ref<Order | null>(null)
const contextMenuRef = ref<InstanceType<typeof ArtMenuRight>>()
const currentRow = ref<Order | null>(null)
// 搜索表单初始值
const initialSearchState: OrderQueryParams = {
order_no: '',
payment_status: undefined,
order_type: undefined,
purchase_role: undefined,
is_expired: undefined,
start_time: '',
end_time: ''
}
// 搜索表单
const searchForm = reactive<OrderQueryParams>({ ...initialSearchState })
// 获取订单角色文本
const getPurchaseRoleText = (role: string): string => {
const roleMap: Record<string, string> = {
self_purchase: '自己购买',
purchased_by_parent: '上级代理购买',
purchased_by_platform: '平台代购',
purchase_for_subordinate: '给下级购买'
}
return roleMap[role] || role
}
// 搜索表单配置
const searchFormItems: SearchFormItem[] = [
{
label: t('orderManagement.searchForm.orderNo'),
prop: 'order_no',
type: 'input',
placeholder: t('orderManagement.searchForm.orderNoPlaceholder'),
config: {
clearable: true
}
},
{
label: t('orderManagement.searchForm.paymentStatus'),
prop: 'payment_status',
type: 'select',
placeholder: t('orderManagement.searchForm.paymentStatusPlaceholder'),
options: [
{ label: t('orderManagement.paymentStatus.pending'), value: 1 },
{ label: t('orderManagement.paymentStatus.paid'), value: 2 },
{ label: t('orderManagement.paymentStatus.cancelled'), value: 3 },
{ label: t('orderManagement.paymentStatus.refunded'), value: 4 }
],
config: {
clearable: true
}
},
{
label: t('orderManagement.searchForm.orderType'),
prop: 'order_type',
type: 'select',
placeholder: t('orderManagement.searchForm.orderTypePlaceholder'),
options: [
{ label: t('orderManagement.orderType.singleCard'), value: 'single_card' },
{ label: t('orderManagement.orderType.device'), value: 'device' }
],
config: {
clearable: true
}
},
{
label: '订单渠道',
prop: 'purchase_role',
type: 'select',
placeholder: '请选择订单渠道',
options: [
{ label: '自己购买', value: 'self_purchase' },
{ label: '上级代理购买', value: 'purchased_by_parent' },
{ label: '平台代购', value: 'purchased_by_platform' },
{ label: '给下级购买', value: 'purchase_for_subordinate' }
],
config: {
clearable: true
}
},
{
label: '超时状态',
prop: 'is_expired',
type: 'select',
placeholder: '请选择超时状态',
options: [
{ label: '已超时', value: true },
{ label: '未超时', value: false }
],
config: {
clearable: true
}
},
{
label: t('orderManagement.searchForm.dateRange'),
prop: 'dateRange',
type: 'daterange',
config: {
clearable: true,
startPlaceholder: t('orderManagement.searchForm.startDate'),
endPlaceholder: t('orderManagement.searchForm.endDate'),
valueFormat: 'YYYY-MM-DD HH:mm:ss'
}
}
]
// 分页
const pagination = reactive({
page: 1,
page_size: 20,
total: 0
})
// 列配置
const columnOptions = [
{ label: t('orderManagement.table.id'), prop: 'id' },
{ label: t('orderManagement.table.orderNo'), prop: 'order_no' },
{ label: t('orderManagement.table.orderType'), prop: 'order_type' },
{ label: t('orderManagement.table.buyerType'), prop: 'buyer_type' },
{ label: '订单渠道', prop: 'purchase_role' },
{ label: '购买备注', prop: 'purchase_remark' },
{ label: '操作者', prop: 'operator_name' },
{ label: t('orderManagement.table.paymentStatus'), prop: 'payment_status' },
{ label: '超时状态', prop: 'is_expired' },
{ label: '超时时间', prop: 'expires_at' },
{ label: t('orderManagement.table.totalAmount'), prop: 'total_amount' },
{ label: '实付金额', prop: 'actual_paid_amount' },
{ label: t('orderManagement.table.paymentMethod'), prop: 'payment_method' },
{ label: t('orderManagement.table.paidAt'), prop: 'paid_at' },
{ label: t('orderManagement.table.createdAt'), prop: 'created_at' }
]
const createFormRef = ref<FormInstance>()
const createRules = reactive<FormRules>({
order_type: [
{
required: true,
message: t('orderManagement.validation.orderTypeRequired'),
trigger: 'change'
}
],
package_ids: [
{
required: true,
message: t('orderManagement.validation.packageIdsRequired'),
trigger: 'change'
}
],
payment_method: [
{
required: true,
message: '请选择支付方式',
trigger: 'change'
}
]
})
const createForm = reactive<CreateOrderRequest>({
order_type: 'single_card',
package_ids: [],
iot_card_id: null,
device_id: null,
payment_method: 'wallet' // 默认使用钱包支付
})
const orderList = ref<Order[]>([])
// 套餐搜索相关
const packageOptions = ref<PackageResponse[]>([])
const packageSearchLoading = ref(false)
// IoT卡搜索相关
const iotCardOptions = ref<StandaloneIotCard[]>([])
const cardSearchLoading = ref(false)
// 设备搜索相关
const deviceOptions = ref<Device[]>([])
const deviceSearchLoading = ref(false)
// 搜索套餐根据套餐名称可选按series_id筛选
const searchPackages = async (query: string, seriesId?: number) => {
packageSearchLoading.value = true
try {
const res = await PackageManageService.getPackages({
package_name: query || undefined,
series_id: seriesId,
page: 1,
page_size: 20,
status: 1, // 只获取启用的套餐
shelf_status: 1 // 只获取已上架的套餐
})
if (res.code === 0) {
packageOptions.value = res.data.items || []
}
} catch (error) {
console.error('Search packages failed:', error)
packageOptions.value = []
} finally {
packageSearchLoading.value = false
}
}
// 套餐远程搜索方法自动使用当前选中的IOT卡/设备的series_id
const handlePackageSearch = (query: string) => {
let seriesId: number | undefined
// 如果是单卡订单并且已选择IOT卡使用该卡的series_id筛选
if (createForm.order_type === 'single_card' && createForm.iot_card_id) {
const selectedCard = iotCardOptions.value.find((card) => card.id === createForm.iot_card_id)
if (selectedCard && selectedCard.series_id) {
seriesId = selectedCard.series_id
}
} else if (createForm.order_type === 'device' && createForm.device_id) {
// 如果是设备订单并且已选择设备使用设备的series_id筛选
const selectedDevice = deviceOptions.value.find((dev) => dev.id === createForm.device_id)
if (selectedDevice && selectedDevice.series_id) {
seriesId = selectedDevice.series_id
}
}
searchPackages(query, seriesId)
}
// 搜索IoT卡根据ICCID
const searchIotCards = async (query: string) => {
cardSearchLoading.value = true
try {
const res = await CardService.getStandaloneIotCards({
iccid: query || undefined,
page: 1,
page_size: 20
})
if (res.code === 0) {
iotCardOptions.value = res.data.items || []
}
} catch (error) {
console.error('Search IoT cards failed:', error)
iotCardOptions.value = []
} finally {
cardSearchLoading.value = false
}
}
// 搜索设备根据设备号virtual_no
const searchDevices = async (query: string) => {
deviceSearchLoading.value = true
try {
const res = await DeviceService.getDevices({
virtual_no: query || undefined,
page: 1,
page_size: 20
})
if (res.code === 0) {
deviceOptions.value = res.data.items || []
}
} catch (error) {
console.error('Search devices failed:', error)
deviceOptions.value = []
} finally {
deviceSearchLoading.value = false
}
}
// 格式化货币 - 将分转换为元
const formatCurrency = (amount: number): string => {
return `¥${(amount / 100).toFixed(2)}`
}
// 获取支付状态标签类型
const getPaymentStatusType = (
status: PaymentStatus
): 'success' | 'info' | 'warning' | 'danger' => {
const statusMap: Record<PaymentStatus, 'success' | 'info' | 'warning' | 'danger'> = {
1: 'warning', // 待支付
2: 'success', // 已支付
3: 'info', // 已取消
4: 'danger' // 已退款
}
return statusMap[status] || 'info'
}
// 获取订单类型文本
const getOrderTypeText = (type: OrderType): string => {
return type === 'single_card'
? t('orderManagement.orderType.singleCard')
: t('orderManagement.orderType.device')
}
// 获取买家类型文本
const getBuyerTypeText = (type: BuyerType): string => {
return type === 'personal'
? t('orderManagement.buyerType.personal')
: t('orderManagement.buyerType.agent')
}
// 获取支付方式文本
const getPaymentMethodText = (method: OrderPaymentMethod): string => {
const methodMap: Record<OrderPaymentMethod, string> = {
wallet: t('orderManagement.paymentMethod.wallet'),
wechat: t('orderManagement.paymentMethod.wechat'),
alipay: t('orderManagement.paymentMethod.alipay')
}
return methodMap[method] || method
}
// 获取佣金状态文本
const getCommissionStatusText = (status: OrderCommissionStatus): string => {
const statusMap: Record<OrderCommissionStatus, string> = {
0: t('orderManagement.commissionStatus.notApplicable'),
1: t('orderManagement.commissionStatus.pending'),
2: t('orderManagement.commissionStatus.settled')
}
return statusMap[status] || '-'
}
// 动态列配置
const { columnChecks, columns } = useCheckedColumns(() => [
{
prop: 'id',
label: t('orderManagement.table.id'),
width: 80
},
{
prop: 'order_no',
label: t('orderManagement.table.orderNo'),
minWidth: 220,
formatter: (row: Order) => {
return h(
'span',
{
style: 'color: var(--el-color-primary); cursor: pointer; text-decoration: underline;',
onClick: (e: MouseEvent) => {
e.stopPropagation()
handleNameClick(row)
}
},
row.order_no
)
}
},
{
prop: 'order_type',
label: t('orderManagement.table.orderType'),
width: 120,
formatter: (row: Order) => {
return h(ElTag, { type: row.order_type === 'single_card' ? 'primary' : 'success' }, () =>
getOrderTypeText(row.order_type)
)
}
},
{
prop: 'buyer_type',
label: t('orderManagement.table.buyerType'),
width: 120,
formatter: (row: Order) => {
return h(ElTag, { type: row.buyer_type === 'personal' ? 'info' : 'warning' }, () =>
getBuyerTypeText(row.buyer_type)
)
}
},
{
prop: 'purchase_role',
label: '订单渠道',
width: 140,
formatter: (row: Order) => {
if (!row.purchase_role) return '-'
const roleTypeMap: Record<string, 'success' | 'info' | 'warning' | 'danger'> = {
self_purchase: 'success',
purchased_by_parent: 'warning',
purchased_by_platform: 'danger',
purchase_for_subordinate: 'info'
}
return h(ElTag, { type: roleTypeMap[row.purchase_role] || 'info', size: 'small' }, () =>
getPurchaseRoleText(row.purchase_role)
)
}
},
{
prop: 'purchase_remark',
label: '购买备注',
minWidth: 180,
showOverflowTooltip: true,
formatter: (row: Order) => row.purchase_remark || '-'
},
{
prop: 'operator_name',
label: '操作者',
width: 120,
formatter: (row: Order) => row.operator_name || '-'
},
{
prop: 'payment_status',
label: t('orderManagement.table.paymentStatus'),
width: 120,
formatter: (row: Order) => {
return h(
ElTag,
{ type: getPaymentStatusType(row.payment_status) },
() => row.payment_status_text
)
}
},
{
prop: 'is_expired',
label: '超时状态',
width: 100,
formatter: (row: Order) => {
if (row.is_expired === undefined || row.is_expired === null) return '-'
if (row.is_expired) {
return h(ElTag, { type: 'danger', size: 'small' }, () => '已超时')
} else {
return h(ElTag, { type: 'success', size: 'small' }, () => '未超时')
}
}
},
{
prop: 'expires_at',
label: '超时时间',
width: 180,
formatter: (row: Order) => (row.expires_at ? formatDateTime(row.expires_at) : '-')
},
{
prop: 'total_amount',
label: t('orderManagement.table.totalAmount'),
width: 120,
formatter: (row: Order) => formatCurrency(row.total_amount)
},
{
prop: 'actual_paid_amount',
label: '实付金额',
width: 120,
formatter: (row: Order) => {
if (row.actual_paid_amount === undefined || row.actual_paid_amount === null) return '-'
return formatCurrency(row.actual_paid_amount)
}
},
{
prop: 'payment_method',
label: t('orderManagement.table.paymentMethod'),
width: 120,
formatter: (row: Order) => getPaymentMethodText(row.payment_method)
},
{
prop: 'paid_at',
label: t('orderManagement.table.paidAt'),
width: 180,
formatter: (row: Order) => (row.paid_at ? formatDateTime(row.paid_at) : '-')
},
{
prop: 'created_at',
label: t('orderManagement.table.createdAt'),
width: 180,
formatter: (row: Order) => formatDateTime(row.created_at)
}
])
// 当选择IOT卡时根据series_id筛选套餐
watch(
() => createForm.iot_card_id,
(newCardId) => {
if (newCardId && createForm.order_type === 'single_card') {
// 找到选中的IOT卡
const selectedCard = iotCardOptions.value.find((card) => card.id === newCardId)
if (selectedCard && selectedCard.series_id) {
// 根据series_id重新加载套餐列表
loadDefaultPackages(selectedCard.series_id)
} else {
// 如果没有series_id加载所有套餐
loadDefaultPackages()
}
}
}
)
// 当选择设备时根据series_id筛选套餐
watch(
() => createForm.device_id,
(newDeviceId) => {
if (newDeviceId && createForm.order_type === 'device') {
// 找到选中的设备
const selectedDevice = deviceOptions.value.find((dev) => dev.id === newDeviceId)
if (selectedDevice && selectedDevice.series_id) {
// 根据series_id重新加载套餐列表
loadDefaultPackages(selectedDevice.series_id)
} else {
// 如果没有series_id加载所有套餐
loadDefaultPackages()
}
}
}
)
onMounted(() => {
getTableData()
})
// 获取订单列表
const getTableData = async () => {
loading.value = true
try {
const params: OrderQueryParams = {
page: pagination.page,
page_size: pagination.page_size,
order_no: searchForm.order_no || undefined,
payment_status: searchForm.payment_status,
order_type: searchForm.order_type,
purchase_role: searchForm.purchase_role,
is_expired: searchForm.is_expired,
start_time: searchForm.start_time || undefined,
end_time: searchForm.end_time || undefined
}
const res = await OrderService.getOrders(params)
if (res.code === 0) {
orderList.value = res.data.list || []
pagination.total = res.data.total || 0
}
} catch (error) {
console.error(error)
} finally {
loading.value = false
}
}
// 重置搜索
const handleReset = () => {
Object.assign(searchForm, { ...initialSearchState })
pagination.page = 1
getTableData()
}
// 搜索
const handleSearch = () => {
// 处理日期范围
if (searchForm.dateRange && Array.isArray(searchForm.dateRange)) {
searchForm.start_time = searchForm.dateRange[0]
searchForm.end_time = searchForm.dateRange[1]
} else {
searchForm.start_time = ''
searchForm.end_time = ''
}
pagination.page = 1
getTableData()
}
// 刷新表格
const handleRefresh = () => {
getTableData()
}
// 处理表格分页变化
const handleSizeChange = (newPageSize: number) => {
pagination.page_size = newPageSize
getTableData()
}
const handleCurrentChange = (newCurrentPage: number) => {
pagination.page = newCurrentPage
getTableData()
}
// 显示创建订单对话框
const showCreateDialog = async () => {
createDialogVisible.value = true
// 加载IoT卡和设备列表套餐列表在选择IoT卡/设备后才加载
await Promise.all([loadDefaultIotCards(), loadDefaultDevices()])
}
// 加载默认套餐列表可选按series_id筛选
const loadDefaultPackages = async (seriesId?: number) => {
packageSearchLoading.value = true
try {
const res = await PackageManageService.getPackages({
series_id: seriesId,
page: 1,
page_size: 20,
status: 1, // 只获取启用的套餐
shelf_status: 1 // 只获取已上架的套餐
})
if (res.code === 0) {
packageOptions.value = res.data.items || []
}
} catch (error) {
console.error('Load default packages failed:', error)
packageOptions.value = []
} finally {
packageSearchLoading.value = false
}
}
// 加载默认IoT卡列表
const loadDefaultIotCards = async () => {
cardSearchLoading.value = true
try {
const res = await CardService.getStandaloneIotCards({
page: 1,
page_size: 20
})
if (res.code === 0) {
iotCardOptions.value = res.data.items || []
}
} catch (error) {
console.error('Load default IoT cards failed:', error)
iotCardOptions.value = []
} finally {
cardSearchLoading.value = false
}
}
// 加载默认设备列表
const loadDefaultDevices = async () => {
deviceSearchLoading.value = true
try {
const res = await DeviceService.getDevices({
page: 1,
page_size: 20
})
if (res.code === 0) {
deviceOptions.value = res.data.items || []
}
} catch (error) {
console.error('Load default devices failed:', error)
deviceOptions.value = []
} finally {
deviceSearchLoading.value = false
}
}
// 对话框关闭后的清理
const handleCreateDialogClosed = () => {
// 重置表单(会同时清除验证状态)
createFormRef.value?.resetFields()
// 重置表单数据到初始值
createForm.order_type = 'single_card'
createForm.package_ids = []
createForm.iot_card_id = null
createForm.device_id = null
createForm.payment_method = 'wallet'
// 清空套餐、IoT卡和设备搜索结果
packageOptions.value = []
iotCardOptions.value = []
deviceOptions.value = []
}
// 创建订单
const handleCreateOrder = async (formEl: FormInstance | undefined) => {
if (!formEl) return
await formEl.validate(async (valid) => {
if (valid) {
createLoading.value = true
try {
// 获取资源ID (IoT卡ID或设备ID)
const resourceId =
createForm.order_type === 'single_card'
? createForm.iot_card_id
: createForm.device_id
if (!resourceId) {
ElMessage.error('请选择IoT卡或设备')
return
}
// 调用套餐购买预检接口
const checkData: PurchaseCheckRequest = {
order_type: createForm.order_type,
package_ids: createForm.package_ids,
resource_id: resourceId
}
const checkResponse = await OrderService.purchaseCheck(checkData)
// 检查预检结果
if (checkResponse.code === 0 && checkResponse.data) {
const checkResult = checkResponse.data
// 如果需要强充,显示确认对话框
if (checkResult.need_force_recharge) {
const confirmMessage = `${checkResult.message || '钱包余额不足'}\n\n套餐总价: ¥${(checkResult.total_package_amount! / 100).toFixed(2)}\n需要强充: ¥${(checkResult.force_recharge_amount! / 100).toFixed(2)}\n钱包到账: ¥${(checkResult.wallet_credit! / 100).toFixed(2)}\n实际支付: ¥${(checkResult.actual_payment! / 100).toFixed(2)}\n\n是否继续创建订单?`
await ElMessageBox.confirm(confirmMessage, '购买预检提示', {
confirmButtonText: '继续创建',
cancelButtonText: '取消',
type: 'warning'
})
}
}
// 预检通过或用户确认后,创建订单
const data: CreateOrderRequest = {
order_type: createForm.order_type,
package_ids: createForm.package_ids,
iot_card_id: createForm.order_type === 'single_card' ? createForm.iot_card_id : null,
device_id: createForm.order_type === 'device' ? createForm.device_id : null,
payment_method: createForm.payment_method // 必填字段
}
await OrderService.createOrder(data)
// 根据支付方式显示不同的成功消息
if (createForm.payment_method === 'wallet') {
ElMessage.success('订单创建成功,已自动完成支付')
} else {
ElMessage.success(t('orderManagement.messages.createSuccess'))
}
createDialogVisible.value = false
formEl.resetFields()
await getTableData()
} catch (error: any) {
// 用户取消确认对话框
if (error === 'cancel') {
return
}
console.error(error)
} finally {
createLoading.value = false
}
}
})
}
// 查看订单详情
const showOrderDetail = (row: Order) => {
router.push({
path: `${RoutesAlias.OrderList}/detail/${row.id}`
})
}
// 处理名称点击
const handleNameClick = (row: Order) => {
if (hasAuth('orders:view_detail')) {
showOrderDetail(row)
} else {
ElMessage.warning('您没有查看详情的权限')
}
}
// 取消订单
const handleCancelOrder = (row: Order) => {
// 已支付的订单不能取消
if (row.payment_status === 2) {
ElMessage.warning(t('orderManagement.messages.cannotCancelPaid'))
return
}
ElMessageBox.confirm(
t('orderManagement.messages.cancelConfirmText'),
t('orderManagement.messages.cancelConfirm'),
{
confirmButtonText: t('orderManagement.actions.confirm'),
cancelButtonText: t('orderManagement.actions.cancel'),
type: 'warning'
}
)
.then(async () => {
try {
await OrderService.cancelOrder(row.id)
ElMessage.success(t('orderManagement.messages.cancelSuccess'))
await getTableData()
} catch (error) {
console.error(error)
}
})
.catch(() => {
// 用户取消操作
})
}
// 右键菜单项配置
const contextMenuItems = computed((): MenuItemType[] => {
if (!currentRow.value) return []
const items: MenuItemType[] = []
// 只有待支付和已支付的订单可以删除
if (
(currentRow.value.payment_status === 1 || currentRow.value.payment_status === 2) &&
hasAuth('orders:delete')
) {
items.push({ key: 'cancel', label: '删除' })
}
return items
})
// 处理表格行右键菜单
const handleRowContextMenu = (row: Order, column: any, event: MouseEvent) => {
event.preventDefault()
event.stopPropagation()
currentRow.value = row
contextMenuRef.value?.show(event)
}
// 处理右键菜单选择
const handleContextMenuSelect = (item: MenuItemType) => {
if (!currentRow.value) return
switch (item.key) {
case 'cancel':
handleCancelOrder(currentRow.value)
break
}
}
</script>
<style scoped lang="scss">
.order-list-page {
height: 100%;
}
.order-detail {
:deep(.el-descriptions__label) {
width: 140px;
}
}
:deep(.el-table__row.table-row-with-context-menu) {
cursor: pointer;
}
</style>