feat: 批量订购上传支付进度与失败明细
All checks were successful
构建并部署前端到测试环境 / build-and-deploy (push) Successful in 8m34s
All checks were successful
构建并部署前端到测试环境 / build-and-deploy (push) Successful in 8m34s
This commit is contained in:
31
src/api/modules/bulkPurchase.ts
Normal file
31
src/api/modules/bulkPurchase.ts
Normal file
@@ -0,0 +1,31 @@
|
||||
import request from '@/utils/http'
|
||||
import type {
|
||||
BulkPurchaseCreateApiResponse,
|
||||
BulkPurchaseItemsApiResponse,
|
||||
BulkPurchaseTaskApiResponse
|
||||
} from '@/types/api'
|
||||
|
||||
export class BulkPurchaseService {
|
||||
static createTask(data: FormData): Promise<BulkPurchaseCreateApiResponse> {
|
||||
return request.post<BulkPurchaseCreateApiResponse>({
|
||||
url: '/api/admin/bulk-purchases',
|
||||
data
|
||||
})
|
||||
}
|
||||
|
||||
static getTask(taskId: number): Promise<BulkPurchaseTaskApiResponse> {
|
||||
return request.get<BulkPurchaseTaskApiResponse>({
|
||||
url: `/api/admin/bulk-purchases/${taskId}`
|
||||
})
|
||||
}
|
||||
|
||||
static getItems(
|
||||
taskId: number,
|
||||
params?: { page?: number; size?: number; status?: string | number }
|
||||
): Promise<BulkPurchaseItemsApiResponse> {
|
||||
return request.get<BulkPurchaseItemsApiResponse>({
|
||||
url: `/api/admin/bulk-purchases/${taskId}/items`,
|
||||
params
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -34,6 +34,7 @@ export { PollingMonitorService } from './pollingMonitor'
|
||||
export { SuperAdminService } from './superAdmin'
|
||||
export { ExportTaskService } from './exportTask'
|
||||
export { OrderPackageInvalidateTaskService } from './orderPackageInvalidateTask'
|
||||
export { BulkPurchaseService } from './bulkPurchase'
|
||||
|
||||
// TODO: 按需添加其他业务模块
|
||||
// export { SettingService } from './setting'
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
drag
|
||||
multiple
|
||||
:auto-upload="false"
|
||||
:accept="accept"
|
||||
:on-change="handleFileChange"
|
||||
:on-remove="handleRemoveFile"
|
||||
list-type="picture"
|
||||
@@ -20,7 +21,7 @@
|
||||
<div class="voucher-upload__text">将{{ voucherName }}拖到此处,或<em>点击上传</em></div>
|
||||
<template #tip>
|
||||
<div class="el-upload__tip">
|
||||
支持多张图片或附件,最多上传 {{ maxCount }} 个文件;可粘贴文件。
|
||||
{{ tip || `支持多张图片或附件,最多上传 ${maxCount} 个文件;可粘贴文件。` }}
|
||||
</div>
|
||||
</template>
|
||||
<template #file="{ file }">
|
||||
@@ -61,11 +62,15 @@
|
||||
modelValue?: string[] | string
|
||||
voucherName?: string
|
||||
maxCount?: number
|
||||
accept?: string
|
||||
tip?: string
|
||||
}
|
||||
|
||||
const props = withDefaults(defineProps<Props>(), {
|
||||
voucherName: '凭证',
|
||||
maxCount: 5
|
||||
maxCount: 5,
|
||||
accept: '',
|
||||
tip: ''
|
||||
})
|
||||
|
||||
const emit = defineEmits<{
|
||||
@@ -171,6 +176,20 @@
|
||||
const file = uploadFile.raw
|
||||
if (!file) return
|
||||
|
||||
if (props.accept) {
|
||||
const accepted = props.accept.split(',').some((type) => {
|
||||
const value = type.trim().toLowerCase()
|
||||
return value.startsWith('.')
|
||||
? file.name.toLowerCase().endsWith(value)
|
||||
: file.type.toLowerCase() === value
|
||||
})
|
||||
if (!accepted) {
|
||||
ElMessage.warning(`只能上传 ${props.accept} 格式的文件`)
|
||||
removeUploadFile(uploadFile)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if (!selectedUploadUids.has(uploadFile.uid)) {
|
||||
if (selectedUploadUids.size >= getMaxCount()) {
|
||||
showMaxCountWarning()
|
||||
|
||||
7
src/config/constants/bulkPurchase.ts
Normal file
7
src/config/constants/bulkPurchase.ts
Normal file
@@ -0,0 +1,7 @@
|
||||
export const BULK_PURCHASE_PERMISSIONS = {
|
||||
page: 'bulk_purchase:view',
|
||||
create: 'bulk_purchase:create',
|
||||
detail: 'bulk_purchase:detail',
|
||||
items: 'bulk_purchase:items',
|
||||
template: 'bulk_purchase:template'
|
||||
} as const
|
||||
@@ -34,3 +34,6 @@ export * from './enableStatus'
|
||||
|
||||
// 导出任务相关
|
||||
export * from './exportTask'
|
||||
|
||||
// 批量订购相关
|
||||
export * from './bulkPurchase'
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { RoutesAlias } from '../routesAlias'
|
||||
import { AppRouteRecord } from '@/types/router'
|
||||
import { BULK_PURCHASE_PERMISSIONS } from '@/config/constants/bulkPurchase'
|
||||
|
||||
/**
|
||||
* 菜单列表、异步路由
|
||||
@@ -525,6 +526,17 @@ export const asyncRoutes: AppRouteRecord[] = [
|
||||
isHide: true,
|
||||
keepAlive: false
|
||||
}
|
||||
},
|
||||
// 批量订购
|
||||
{
|
||||
path: 'bulk-purchase',
|
||||
name: 'BulkPurchase',
|
||||
component: RoutesAlias.BulkPurchase,
|
||||
meta: {
|
||||
title: '批量订购套餐',
|
||||
permissions: [BULK_PURCHASE_PERMISSIONS.page],
|
||||
keepAlive: true
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
|
||||
@@ -68,6 +68,7 @@ export enum RoutesAlias {
|
||||
// 订单管理
|
||||
OrderList = '/order-management/order-list', // 订单列表
|
||||
OrderDetail = '/order-management/order-list/detail', // 订单详情
|
||||
BulkPurchase = '/order-management/bulk-purchase', // 批量订购
|
||||
|
||||
// 财务管理
|
||||
AgentRecharge = '/finance/agent-recharge', // 代理充值
|
||||
|
||||
67
src/types/api/bulkPurchase.ts
Normal file
67
src/types/api/bulkPurchase.ts
Normal file
@@ -0,0 +1,67 @@
|
||||
import type { BaseResponse } from './common'
|
||||
|
||||
export type BulkPurchasePaymentMethod = 'wallet' | 'offline'
|
||||
|
||||
export enum BulkPurchaseTaskStatus {
|
||||
PENDING = 1,
|
||||
PROCESSING = 2,
|
||||
COMPLETED = 3,
|
||||
FAILED = 4,
|
||||
CANCELED = 5
|
||||
}
|
||||
|
||||
export interface BulkPurchaseCreateResponse {
|
||||
task_id: number
|
||||
task_no?: string
|
||||
status?: BulkPurchaseTaskStatus
|
||||
status_name?: string
|
||||
message?: string
|
||||
}
|
||||
|
||||
export interface BulkPurchaseTask {
|
||||
id?: number
|
||||
task_id: number
|
||||
task_no?: string
|
||||
status: BulkPurchaseTaskStatus
|
||||
status_name?: string
|
||||
total_count?: number
|
||||
success_count?: number
|
||||
failed_count?: number
|
||||
total_amount?: number
|
||||
success_amount?: number
|
||||
failed_amount?: number
|
||||
amount_summary?: Record<string, number | string>
|
||||
error_code?: string
|
||||
error_summary?: string
|
||||
created_at?: string
|
||||
updated_at?: string
|
||||
started_at?: string
|
||||
completed_at?: string
|
||||
}
|
||||
|
||||
export interface BulkPurchaseItem {
|
||||
id?: number
|
||||
row_number?: number
|
||||
line?: number
|
||||
asset_identifier?: string
|
||||
iccid?: string
|
||||
virtual_no?: string
|
||||
package_code?: string
|
||||
status?: string | number
|
||||
status_name?: string
|
||||
error_code?: string
|
||||
error_reason?: string
|
||||
error_summary?: string
|
||||
}
|
||||
|
||||
export interface BulkPurchaseItemsResponse {
|
||||
items: BulkPurchaseItem[]
|
||||
page?: number
|
||||
size?: number
|
||||
page_size?: number
|
||||
total?: number
|
||||
}
|
||||
|
||||
export type BulkPurchaseCreateApiResponse = BaseResponse<BulkPurchaseCreateResponse>
|
||||
export type BulkPurchaseTaskApiResponse = BaseResponse<BulkPurchaseTask>
|
||||
export type BulkPurchaseItemsApiResponse = BaseResponse<BulkPurchaseItemsResponse>
|
||||
@@ -116,3 +116,6 @@ export * from './asyncTask'
|
||||
|
||||
// 订单套餐批量作废任务相关
|
||||
export * from './orderPackageInvalidateTask'
|
||||
|
||||
// 批量订购任务相关
|
||||
export * from './bulkPurchase'
|
||||
|
||||
495
src/views/order-management/bulk-purchase/index.vue
Normal file
495
src/views/order-management/bulk-purchase/index.vue
Normal file
@@ -0,0 +1,495 @@
|
||||
<template>
|
||||
<ArtTableFullScreen>
|
||||
<div class="bulk-purchase-page" id="table-full-screen">
|
||||
<ElCard shadow="never" class="art-table-card">
|
||||
<template #header>
|
||||
<div class="page-header">
|
||||
<div>
|
||||
<h2>批量订购套餐</h2>
|
||||
<p>同一批次只能选择一种支付方式,系统按任务结果逐行处理订单。</p>
|
||||
</div>
|
||||
<ElButton
|
||||
v-if="hasAuth(BULK_PURCHASE_PERMISSIONS.template)"
|
||||
tag="a"
|
||||
href="/templates/bulk-purchase-template.xlsx"
|
||||
download="批量订购模板.xlsx"
|
||||
>
|
||||
下载模板
|
||||
</ElButton>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<ElForm ref="formRef" :model="form" :rules="rules" label-width="110px" class="create-form">
|
||||
<ElFormItem label="代理商" prop="shop_id">
|
||||
<ElSelect
|
||||
v-model="form.shop_id"
|
||||
filterable
|
||||
remote
|
||||
reserve-keyword
|
||||
clearable
|
||||
placeholder="请选择代理商"
|
||||
:remote-method="searchShops"
|
||||
:loading="shopLoading"
|
||||
style="width: 100%"
|
||||
>
|
||||
<ElOption
|
||||
v-for="shop in shopOptions"
|
||||
:key="shop.id"
|
||||
:label="`${shop.shop_name} (${shop.shop_code})`"
|
||||
:value="shop.id"
|
||||
/>
|
||||
</ElSelect>
|
||||
</ElFormItem>
|
||||
|
||||
<ElFormItem label="支付方式" prop="payment_method">
|
||||
<ElRadioGroup v-model="form.payment_method" @change="handlePaymentMethodChange">
|
||||
<ElRadio value="wallet">代理钱包</ElRadio>
|
||||
<ElRadio value="offline">线下支付</ElRadio>
|
||||
</ElRadioGroup>
|
||||
</ElFormItem>
|
||||
|
||||
<ElFormItem label="订单文件" prop="orderFile">
|
||||
<VoucherUpload
|
||||
ref="orderUploadRef"
|
||||
v-model="orderFileKeys"
|
||||
voucher-name="订单文件"
|
||||
:max-count="1"
|
||||
accept=".xlsx,.xls,.csv"
|
||||
tip="支持 .xlsx、.xls 或 .csv 文件"
|
||||
@uploading-change="orderFileUploading = $event"
|
||||
@change="formRef?.validateField('orderFile')"
|
||||
/>
|
||||
</ElFormItem>
|
||||
|
||||
<ElFormItem
|
||||
v-if="form.payment_method === 'offline'"
|
||||
label="整批支付凭证"
|
||||
prop="voucherFile"
|
||||
>
|
||||
<VoucherUpload
|
||||
ref="voucherUploadRef"
|
||||
v-model="voucherFileKeys"
|
||||
voucher-name="整批支付凭证"
|
||||
@uploading-change="voucherUploading = $event"
|
||||
@change="formRef?.validateField('voucherFile')"
|
||||
/>
|
||||
</ElFormItem>
|
||||
|
||||
<ElAlert
|
||||
v-if="submitting || voucherUploading || orderFileUploading"
|
||||
type="info"
|
||||
:closable="false"
|
||||
show-icon
|
||||
:title="
|
||||
voucherUploading
|
||||
? '支付凭证上传中,请稍候'
|
||||
: orderFileUploading
|
||||
? '订单文件上传中,请稍候'
|
||||
: '批量订购文件上传及任务创建中,请勿重复提交'
|
||||
"
|
||||
/>
|
||||
|
||||
<div class="form-actions">
|
||||
<ElButton
|
||||
type="primary"
|
||||
:loading="submitting || voucherUploading || orderFileUploading"
|
||||
:disabled="
|
||||
voucherUploading || orderFileUploading || !hasAuth(BULK_PURCHASE_PERMISSIONS.create)
|
||||
"
|
||||
@click="submitTask"
|
||||
>
|
||||
创建批量订购任务
|
||||
</ElButton>
|
||||
<ElButton v-if="taskDetail" @click="resetTask">重新创建</ElButton>
|
||||
</div>
|
||||
</ElForm>
|
||||
</ElCard>
|
||||
|
||||
<ElCard
|
||||
v-if="taskDetail && hasAuth(BULK_PURCHASE_PERMISSIONS.detail)"
|
||||
shadow="never"
|
||||
class="art-table-card task-card"
|
||||
>
|
||||
<template #header>
|
||||
<div class="task-header">
|
||||
<span>任务结果</span>
|
||||
<ElTag :type="getStatusType(taskDetail.status)">
|
||||
{{ taskDetail.status_name || getStatusName(taskDetail.status) }}
|
||||
</ElTag>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<ElDescriptions :column="4" border>
|
||||
<ElDescriptionsItem label="任务号">{{
|
||||
taskDetail.task_no || taskDetail.task_id
|
||||
}}</ElDescriptionsItem>
|
||||
<ElDescriptionsItem label="总数">{{ taskDetail.total_count ?? 0 }}</ElDescriptionsItem>
|
||||
<ElDescriptionsItem label="成功数">{{
|
||||
taskDetail.success_count ?? 0
|
||||
}}</ElDescriptionsItem>
|
||||
<ElDescriptionsItem label="失败数">{{ taskDetail.failed_count ?? 0 }}</ElDescriptionsItem>
|
||||
<ElDescriptionsItem label="总金额">{{
|
||||
formatAmount(taskDetail.total_amount)
|
||||
}}</ElDescriptionsItem>
|
||||
<ElDescriptionsItem label="成功金额">{{
|
||||
formatAmount(taskDetail.success_amount)
|
||||
}}</ElDescriptionsItem>
|
||||
<ElDescriptionsItem label="失败金额">{{
|
||||
formatAmount(taskDetail.failed_amount)
|
||||
}}</ElDescriptionsItem>
|
||||
<ElDescriptionsItem label="更新时间">{{
|
||||
formatDateTime(taskDetail.updated_at)
|
||||
}}</ElDescriptionsItem>
|
||||
</ElDescriptions>
|
||||
|
||||
<ElAlert
|
||||
v-if="taskDetail.error_summary"
|
||||
class="task-error"
|
||||
type="error"
|
||||
:closable="false"
|
||||
:title="taskDetail.error_summary"
|
||||
/>
|
||||
|
||||
<div v-if="hasAuth(BULK_PURCHASE_PERMISSIONS.items)" class="items-toolbar">
|
||||
<ElSelect v-model="itemStatus" clearable placeholder="筛选行状态" @change="reloadItems">
|
||||
<ElOption label="成功" value="success" />
|
||||
<ElOption label="失败" value="failed" />
|
||||
<ElOption label="处理中" value="processing" />
|
||||
</ElSelect>
|
||||
<ElButton @click="reloadItems">刷新结果</ElButton>
|
||||
</div>
|
||||
|
||||
<ElTable
|
||||
v-if="hasAuth(BULK_PURCHASE_PERMISSIONS.items)"
|
||||
v-loading="itemsLoading"
|
||||
:data="items"
|
||||
border
|
||||
>
|
||||
<ElTableColumn label="行号" width="90">
|
||||
<template #default="scope">{{
|
||||
scope.row.row_number ?? scope.row.line ?? '-'
|
||||
}}</template>
|
||||
</ElTableColumn>
|
||||
<ElTableColumn label="资产" min-width="180">
|
||||
<template #default="scope">{{ getAssetIdentifier(scope.row) }}</template>
|
||||
</ElTableColumn>
|
||||
<ElTableColumn prop="package_code" label="套餐编码" min-width="150" />
|
||||
<ElTableColumn label="状态" width="110">
|
||||
<template #default="scope">{{
|
||||
scope.row.status_name || scope.row.status || '-'
|
||||
}}</template>
|
||||
</ElTableColumn>
|
||||
<ElTableColumn label="错误原因" min-width="240" show-overflow-tooltip>
|
||||
<template #default="scope">
|
||||
{{ scope.row.error_summary || scope.row.error_reason || scope.row.error_code || '-' }}
|
||||
</template>
|
||||
</ElTableColumn>
|
||||
</ElTable>
|
||||
|
||||
<div v-if="hasAuth(BULK_PURCHASE_PERMISSIONS.items)" class="pagination-wrapper">
|
||||
<ElPagination
|
||||
v-model:current-page="itemsPage"
|
||||
v-model:page-size="itemsSize"
|
||||
layout="total, sizes, prev, pager, next"
|
||||
:total="itemsTotal"
|
||||
@current-change="loadItems"
|
||||
@size-change="reloadItems"
|
||||
/>
|
||||
</div>
|
||||
</ElCard>
|
||||
</div>
|
||||
</ArtTableFullScreen>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { computed, onMounted, reactive, ref, watch } from 'vue'
|
||||
import { useRoute, useRouter } from 'vue-router'
|
||||
import {
|
||||
ElAlert,
|
||||
ElButton,
|
||||
ElCard,
|
||||
ElDescriptions,
|
||||
ElDescriptionsItem,
|
||||
ElForm,
|
||||
ElMessage,
|
||||
ElOption,
|
||||
ElPagination,
|
||||
ElRadio,
|
||||
ElRadioGroup,
|
||||
ElSelect,
|
||||
ElTable,
|
||||
ElTableColumn,
|
||||
ElTag
|
||||
} from 'element-plus'
|
||||
import type { FormInstance, FormRules } from 'element-plus'
|
||||
import VoucherUpload from '@/components/business/VoucherUpload.vue'
|
||||
import { BulkPurchaseService, ShopService } from '@/api/modules'
|
||||
import type {
|
||||
BulkPurchaseItem,
|
||||
BulkPurchasePaymentMethod,
|
||||
BulkPurchaseTask,
|
||||
ShopResponse
|
||||
} from '@/types/api'
|
||||
import { BulkPurchaseTaskStatus } from '@/types/api'
|
||||
import { useAuth } from '@/composables/useAuth'
|
||||
import { useAsyncTaskPolling } from '@/composables/useAsyncTaskPolling'
|
||||
import { formatDateTime, formatMoney } from '@/utils/business/format'
|
||||
import { BULK_PURCHASE_PERMISSIONS } from '@/config/constants/bulkPurchase'
|
||||
|
||||
defineOptions({ name: 'BulkPurchase' })
|
||||
|
||||
const route = useRoute()
|
||||
const router = useRouter()
|
||||
const { hasAuth } = useAuth()
|
||||
const formRef = ref<FormInstance>()
|
||||
const orderUploadRef = ref<InstanceType<typeof VoucherUpload>>()
|
||||
const voucherUploadRef = ref<InstanceType<typeof VoucherUpload>>()
|
||||
const shopOptions = ref<ShopResponse[]>([])
|
||||
const shopLoading = ref(false)
|
||||
const submitting = ref(false)
|
||||
const orderFileKeys = ref<string[]>([])
|
||||
const orderFileUploading = ref(false)
|
||||
const voucherFileKeys = ref<string[]>([])
|
||||
const voucherUploading = ref(false)
|
||||
const itemStatus = ref<string | undefined>()
|
||||
const items = ref<BulkPurchaseItem[]>([])
|
||||
const itemsLoading = ref(false)
|
||||
const itemsPage = ref(1)
|
||||
const itemsSize = ref(20)
|
||||
const itemsTotal = ref(0)
|
||||
const requestId = ref('')
|
||||
|
||||
const form = reactive<{
|
||||
shop_id?: number
|
||||
payment_method: BulkPurchasePaymentMethod
|
||||
}>({
|
||||
shop_id: undefined,
|
||||
payment_method: 'wallet'
|
||||
})
|
||||
|
||||
const rules = computed<FormRules>(() => ({
|
||||
shop_id: [{ required: true, message: '请选择代理商', trigger: 'change' }],
|
||||
payment_method: [{ required: true, message: '请选择支付方式', trigger: 'change' }],
|
||||
orderFile: [
|
||||
{
|
||||
validator: (_rule: unknown, _value: unknown, callback: (error?: Error) => void) => {
|
||||
if (orderFileKeys.value.length === 0) callback(new Error('请上传订单文件'))
|
||||
else callback()
|
||||
},
|
||||
trigger: 'change'
|
||||
}
|
||||
],
|
||||
voucherFile: [
|
||||
{
|
||||
validator: (_rule: unknown, _value: unknown, callback: (error?: Error) => void) => {
|
||||
if (form.payment_method === 'offline' && voucherFileKeys.value.length === 0) {
|
||||
callback(new Error('线下支付必须上传整批支付凭证'))
|
||||
} else callback()
|
||||
},
|
||||
trigger: 'change'
|
||||
}
|
||||
]
|
||||
}))
|
||||
|
||||
const polling = useAsyncTaskPolling<BulkPurchaseTask>({
|
||||
storageKey: 'bulk-purchase-active-task',
|
||||
fetchTask: async (taskId) => {
|
||||
const res = await BulkPurchaseService.getTask(taskId)
|
||||
if (res.code !== 0 || !res.data) throw new Error(res.msg || '获取批量订购任务失败')
|
||||
return res.data
|
||||
}
|
||||
})
|
||||
const taskDetail = polling.task
|
||||
|
||||
const getRequestId = () => {
|
||||
if (requestId.value) return requestId.value
|
||||
requestId.value =
|
||||
typeof crypto !== 'undefined' && crypto.randomUUID
|
||||
? crypto.randomUUID()
|
||||
: `${Date.now()}-${Math.random().toString(16).slice(2)}`
|
||||
return requestId.value
|
||||
}
|
||||
|
||||
const searchShops = async (query: string) => {
|
||||
shopLoading.value = true
|
||||
try {
|
||||
const res = await ShopService.getShops({
|
||||
page: 1,
|
||||
page_size: 20,
|
||||
shop_name: query || undefined
|
||||
})
|
||||
if (res.code === 0) shopOptions.value = res.data.items || []
|
||||
} finally {
|
||||
shopLoading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
const handlePaymentMethodChange = (value: string | number | boolean | undefined) => {
|
||||
if (value === 'wallet') {
|
||||
voucherFileKeys.value = []
|
||||
voucherUploadRef.value?.clearFiles()
|
||||
formRef.value?.clearValidate('voucherFile')
|
||||
}
|
||||
}
|
||||
|
||||
const submitTask = async () => {
|
||||
if (
|
||||
!hasAuth(BULK_PURCHASE_PERMISSIONS.create) ||
|
||||
submitting.value ||
|
||||
voucherUploading.value ||
|
||||
orderFileUploading.value
|
||||
)
|
||||
return
|
||||
const valid = await formRef.value?.validate().catch(() => false)
|
||||
if (!valid || !form.shop_id || orderFileKeys.value.length === 0) return
|
||||
|
||||
submitting.value = true
|
||||
try {
|
||||
const data = new FormData()
|
||||
data.append('shop_id', String(form.shop_id))
|
||||
data.append('payment_method', form.payment_method)
|
||||
data.append('file', orderFileKeys.value[0])
|
||||
data.append('request_id', getRequestId())
|
||||
if (form.payment_method === 'offline' && voucherFileKeys.value.length > 0) {
|
||||
data.append('voucher_file', voucherFileKeys.value[0])
|
||||
}
|
||||
|
||||
const res = await BulkPurchaseService.createTask(data)
|
||||
if (res.code !== 0 || !res.data?.task_id) {
|
||||
ElMessage.error(res.msg || '创建批量订购任务失败')
|
||||
return
|
||||
}
|
||||
|
||||
ElMessage.success(res.data.message || '批量订购任务已创建')
|
||||
requestId.value = ''
|
||||
await router.replace({
|
||||
path: route.path,
|
||||
query: { task_id: String(res.data.task_id) }
|
||||
})
|
||||
await polling.start(res.data.task_id)
|
||||
await loadItems()
|
||||
} catch (error: any) {
|
||||
ElMessage.error(error?.message || '创建批量订购任务失败')
|
||||
} finally {
|
||||
submitting.value = false
|
||||
}
|
||||
}
|
||||
|
||||
const loadItems = async () => {
|
||||
const taskId = taskDetail.value?.task_id
|
||||
if (!taskId || !hasAuth(BULK_PURCHASE_PERMISSIONS.items)) return
|
||||
itemsLoading.value = true
|
||||
try {
|
||||
const res = await BulkPurchaseService.getItems(taskId, {
|
||||
page: itemsPage.value,
|
||||
size: itemsSize.value,
|
||||
status: itemStatus.value
|
||||
})
|
||||
if (res.code === 0 && res.data) {
|
||||
items.value = res.data.items || []
|
||||
itemsTotal.value = res.data.total || 0
|
||||
} else {
|
||||
ElMessage.error(res.msg || '获取批量订购明细失败')
|
||||
}
|
||||
} catch (error: any) {
|
||||
ElMessage.error(error?.message || '获取批量订购明细失败')
|
||||
} finally {
|
||||
itemsLoading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
const reloadItems = () => {
|
||||
itemsPage.value = 1
|
||||
void loadItems()
|
||||
}
|
||||
|
||||
const resetTask = () => {
|
||||
polling.clear()
|
||||
requestId.value = ''
|
||||
items.value = []
|
||||
itemsTotal.value = 0
|
||||
itemsPage.value = 1
|
||||
router.replace({ path: route.path })
|
||||
}
|
||||
|
||||
const getStatusName = (status: BulkPurchaseTaskStatus) => {
|
||||
const names: Record<BulkPurchaseTaskStatus, string> = {
|
||||
[BulkPurchaseTaskStatus.PENDING]: '待处理',
|
||||
[BulkPurchaseTaskStatus.PROCESSING]: '处理中',
|
||||
[BulkPurchaseTaskStatus.COMPLETED]: '已完成',
|
||||
[BulkPurchaseTaskStatus.FAILED]: '已失败',
|
||||
[BulkPurchaseTaskStatus.CANCELED]: '已取消'
|
||||
}
|
||||
return names[status] || '-'
|
||||
}
|
||||
|
||||
const getStatusType = (status: BulkPurchaseTaskStatus) => {
|
||||
if (status === BulkPurchaseTaskStatus.COMPLETED) return 'success'
|
||||
if (status === BulkPurchaseTaskStatus.FAILED) return 'danger'
|
||||
if (status === BulkPurchaseTaskStatus.PROCESSING) return 'warning'
|
||||
return 'info'
|
||||
}
|
||||
|
||||
const formatAmount = (amount?: number) => (amount === undefined ? '-' : formatMoney(amount))
|
||||
|
||||
const getAssetIdentifier = (item: BulkPurchaseItem) =>
|
||||
item.asset_identifier || item.iccid || item.virtual_no || '-'
|
||||
|
||||
watch(taskDetail, () => {
|
||||
itemsPage.value = 1
|
||||
void loadItems()
|
||||
})
|
||||
|
||||
onMounted(() => {
|
||||
if (hasAuth(BULK_PURCHASE_PERMISSIONS.create)) void searchShops('')
|
||||
const routeTaskId = Number(route.query.task_id)
|
||||
if (routeTaskId && routeTaskId !== polling.taskId.value) void polling.start(routeTaskId)
|
||||
})
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.bulk-purchase-page {
|
||||
.page-header,
|
||||
.task-header,
|
||||
.form-actions,
|
||||
.items-toolbar {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.page-header h2 {
|
||||
margin: 0;
|
||||
font-size: 20px;
|
||||
}
|
||||
|
||||
.page-header p {
|
||||
margin: 8px 0 0;
|
||||
color: var(--el-text-color-secondary);
|
||||
}
|
||||
|
||||
.create-form {
|
||||
max-width: 760px;
|
||||
}
|
||||
|
||||
.task-card {
|
||||
margin-top: 16px;
|
||||
}
|
||||
|
||||
.task-error {
|
||||
margin-top: 16px;
|
||||
}
|
||||
|
||||
.items-toolbar {
|
||||
justify-content: flex-start;
|
||||
margin: 20px 0 12px;
|
||||
}
|
||||
|
||||
.pagination-wrapper {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
margin-top: 16px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -22,6 +22,13 @@
|
||||
<ElButton @click="showCreateDialog" v-permission="'orders:add'">{{
|
||||
'创建订单'
|
||||
}}</ElButton>
|
||||
<ElButton
|
||||
v-if="hasAuth(BULK_PURCHASE_PERMISSIONS.create)"
|
||||
type="primary"
|
||||
@click="router.push(RoutesAlias.BulkPurchase)"
|
||||
>
|
||||
批量订购
|
||||
</ElButton>
|
||||
<ElButton v-if="hasAuth('orders:export')" @click="exportDialogVisible = true">{{
|
||||
'导出'
|
||||
}}</ElButton>
|
||||
@@ -278,6 +285,7 @@
|
||||
import PaymentVoucherDialog from '@/components/business/PaymentVoucherDialog.vue'
|
||||
import ExportTaskCreateDialog from '@/components/business/ExportTaskCreateDialog.vue'
|
||||
import VoucherUpload from '@/components/business/VoucherUpload.vue'
|
||||
import { BULK_PURCHASE_PERMISSIONS } from '@/config/constants/bulkPurchase'
|
||||
|
||||
defineOptions({ name: 'OrderList' })
|
||||
|
||||
|
||||
Reference in New Issue
Block a user