This commit is contained in:
@@ -10,7 +10,8 @@ export interface ExchangeQueryParams {
|
||||
page?: number
|
||||
page_size?: number
|
||||
status?: number // 换货状态
|
||||
identifier?: string // 资产标识符(模糊匹配)
|
||||
flow_type?: string // 流程类型(shipping/direct)
|
||||
identifier?: string // 资产标识符(模糊匹配,同时匹配旧资产、新资产)
|
||||
created_at_start?: string // 创建时间起始
|
||||
created_at_end?: string // 创建时间结束
|
||||
}
|
||||
@@ -20,6 +21,9 @@ export interface CreateExchangeRequest {
|
||||
exchange_reason: string // 换货原因
|
||||
old_asset_type: string // 旧资产类型 (iot_card 或 device)
|
||||
old_identifier: string // 旧资产标识符(ICCID/虚拟号/IMEI/SN)
|
||||
flow_type?: string // 流程类型(shipping/direct),默认 shipping
|
||||
new_identifier?: string // 新资产标识符(direct 时必填)
|
||||
migrate_data?: boolean // 是否迁移数据(默认 false)
|
||||
remark?: string // 备注(可选)
|
||||
}
|
||||
|
||||
@@ -34,6 +38,10 @@ export interface ExchangeResponse {
|
||||
new_asset_identifier: string
|
||||
status: number // 换货状态(1:待填写信息, 2:待发货, 3:已发货待确认, 4:已完成, 5:已取消)
|
||||
status_text: string
|
||||
flow_type: string // 流程类型(shipping/direct)
|
||||
flow_type_name: string // 流程类型名称
|
||||
shipped_at?: string | null // 发货时间(仅 shipping 发货后有值)
|
||||
completed_at?: string | null // 换货完成时间
|
||||
recipient_name?: string
|
||||
recipient_phone?: string
|
||||
recipient_address?: string
|
||||
|
||||
@@ -55,9 +55,15 @@
|
||||
return types[status] || 'info'
|
||||
}
|
||||
|
||||
// 详情页配置
|
||||
const detailSections: DetailSection[] = [
|
||||
{
|
||||
// 根据流程类型动态生成详情页配置
|
||||
const detailSections = computed<DetailSection[]>(() => {
|
||||
if (!exchangeDetail.value) return []
|
||||
|
||||
const flowType = exchangeDetail.value.flow_type || 'shipping' // 历史数据兼容
|
||||
const sections: DetailSection[] = []
|
||||
|
||||
// 基本信息
|
||||
sections.push({
|
||||
title: '基本信息',
|
||||
fields: [
|
||||
{ label: '换货单号', prop: 'exchange_no' },
|
||||
@@ -65,6 +71,10 @@
|
||||
label: '状态',
|
||||
render: (data) => h(ElTag, { type: getStatusType(data.status) }, () => data.status_text)
|
||||
},
|
||||
{
|
||||
label: '流程类型',
|
||||
formatter: (_, data) => data.flow_type_name || (flowType === 'direct' ? '直接换货' : '物流换货')
|
||||
},
|
||||
{
|
||||
label: '换货原因',
|
||||
prop: 'exchange_reason',
|
||||
@@ -86,44 +96,57 @@
|
||||
prop: 'new_asset_identifier'
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
title: '收货信息',
|
||||
fields: [
|
||||
{
|
||||
label: '收货人姓名',
|
||||
formatter: (value) => value || '--',
|
||||
prop: 'recipient_name'
|
||||
},
|
||||
{
|
||||
label: '收货人电话',
|
||||
formatter: (value) => value || '--',
|
||||
prop: 'recipient_phone'
|
||||
},
|
||||
{
|
||||
label: '收货地址',
|
||||
formatter: (value) => value || '--',
|
||||
prop: 'recipient_address',
|
||||
fullWidth: true
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
title: '物流信息',
|
||||
fields: [
|
||||
{
|
||||
label: '快递公司',
|
||||
formatter: (value) => value || '--',
|
||||
prop: 'express_company'
|
||||
},
|
||||
{
|
||||
label: '快递单号',
|
||||
formatter: (value) => value || '--',
|
||||
prop: 'express_no'
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
})
|
||||
|
||||
// 收货信息(仅物流换货显示)
|
||||
if (flowType === 'shipping') {
|
||||
sections.push({
|
||||
title: '收货信息',
|
||||
fields: [
|
||||
{
|
||||
label: '收货人姓名',
|
||||
formatter: (value) => value || '--',
|
||||
prop: 'recipient_name'
|
||||
},
|
||||
{
|
||||
label: '收货人电话',
|
||||
formatter: (value) => value || '--',
|
||||
prop: 'recipient_phone'
|
||||
},
|
||||
{
|
||||
label: '收货地址',
|
||||
formatter: (value) => value || '--',
|
||||
prop: 'recipient_address',
|
||||
fullWidth: true
|
||||
}
|
||||
]
|
||||
})
|
||||
|
||||
// 物流信息(仅物流换货显示)
|
||||
sections.push({
|
||||
title: '物流信息',
|
||||
fields: [
|
||||
{
|
||||
label: '快递公司',
|
||||
formatter: (value) => value || '--',
|
||||
prop: 'express_company'
|
||||
},
|
||||
{
|
||||
label: '快递单号',
|
||||
formatter: (value) => value || '--',
|
||||
prop: 'express_no'
|
||||
},
|
||||
{
|
||||
label: '发货时间',
|
||||
formatter: (value) => (value ? formatDateTime(value) : '--'),
|
||||
prop: 'shipped_at'
|
||||
}
|
||||
]
|
||||
})
|
||||
}
|
||||
|
||||
// 其他信息
|
||||
sections.push({
|
||||
title: '其他信息',
|
||||
fields: [
|
||||
{
|
||||
@@ -132,6 +155,11 @@
|
||||
prop: 'remark',
|
||||
fullWidth: true
|
||||
},
|
||||
{
|
||||
label: '完成时间',
|
||||
formatter: (value) => (value ? formatDateTime(value) : '--'),
|
||||
prop: 'completed_at'
|
||||
},
|
||||
{
|
||||
label: '创建时间',
|
||||
formatter: (value) => formatDateTime(value),
|
||||
@@ -143,8 +171,10 @@
|
||||
prop: 'updated_at'
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
})
|
||||
|
||||
return sections
|
||||
})
|
||||
|
||||
// 加载换货单详情
|
||||
const loadExchangeDetail = async () => {
|
||||
|
||||
@@ -50,6 +50,25 @@
|
||||
@closed="handleCloseCreateDialog"
|
||||
>
|
||||
<ElForm ref="createFormRef" :model="createForm" :rules="createRules" label-width="120px">
|
||||
<ElFormItem label="流程类型" prop="flow_type">
|
||||
<ElSelect
|
||||
v-model="createForm.flow_type"
|
||||
placeholder="请选择流程类型"
|
||||
style="width: 100%"
|
||||
@change="handleFlowTypeChange"
|
||||
>
|
||||
<ElOption label="物流换货" value="shipping" />
|
||||
<ElOption label="直接换货" value="direct" />
|
||||
</ElSelect>
|
||||
<div style="margin-top: 4px; font-size: 12px; color: #909399">
|
||||
<span v-if="createForm.flow_type === 'shipping'">
|
||||
物流换货:需要填写收货信息、发货等步骤
|
||||
</span>
|
||||
<span v-else-if="createForm.flow_type === 'direct'">
|
||||
直接换货:创建后自动完成,无需物流环节
|
||||
</span>
|
||||
</div>
|
||||
</ElFormItem>
|
||||
<ElFormItem label="换货原因" prop="exchange_reason">
|
||||
<ElSelect
|
||||
v-model="createForm.exchange_reason"
|
||||
@@ -151,6 +170,74 @@
|
||||
</ElOption>
|
||||
</ElSelect>
|
||||
</ElFormItem>
|
||||
<ElFormItem
|
||||
v-if="createForm.flow_type === 'direct' && createForm.old_asset_type === 'iot_card'"
|
||||
label="新资产标识符"
|
||||
prop="new_identifier"
|
||||
>
|
||||
<ElSelect
|
||||
v-model="createForm.new_identifier"
|
||||
filterable
|
||||
remote
|
||||
reserve-keyword
|
||||
placeholder="请输入ICCID搜索"
|
||||
:remote-method="searchNewIotCards"
|
||||
:loading="newCardSearchLoading"
|
||||
style="width: 100%"
|
||||
clearable
|
||||
>
|
||||
<ElOption
|
||||
v-for="card in newIotCardOptions"
|
||||
:key="card.id"
|
||||
:label="`${card.iccid} (${card.msisdn || '无接入号'})`"
|
||||
:value="card.iccid"
|
||||
>
|
||||
<div style="display: flex; justify-content: space-between">
|
||||
<span>{{ card.iccid }}</span>
|
||||
<span style="font-size: 12px; color: var(--el-text-color-secondary)">
|
||||
{{ card.msisdn || '无接入号' }}
|
||||
</span>
|
||||
</div>
|
||||
</ElOption>
|
||||
</ElSelect>
|
||||
</ElFormItem>
|
||||
<ElFormItem
|
||||
v-if="createForm.flow_type === 'direct' && createForm.old_asset_type === 'device'"
|
||||
label="新资产标识符"
|
||||
prop="new_identifier"
|
||||
>
|
||||
<ElSelect
|
||||
v-model="createForm.new_identifier"
|
||||
filterable
|
||||
remote
|
||||
reserve-keyword
|
||||
placeholder="请输入虚拟号或IMEI搜索"
|
||||
:remote-method="searchNewDevices"
|
||||
:loading="newDeviceSearchLoading"
|
||||
style="width: 100%"
|
||||
clearable
|
||||
>
|
||||
<ElOption
|
||||
v-for="device in newDeviceOptions"
|
||||
:key="device.id"
|
||||
:label="`${device.virtual_no} (${device.device_name || device.imei})`"
|
||||
:value="device.virtual_no"
|
||||
>
|
||||
<div style="display: flex; justify-content: space-between">
|
||||
<span>{{ device.virtual_no }}</span>
|
||||
<span style="font-size: 12px; color: var(--el-text-color-secondary)">
|
||||
{{ device.device_name || device.imei }}
|
||||
</span>
|
||||
</div>
|
||||
</ElOption>
|
||||
</ElSelect>
|
||||
</ElFormItem>
|
||||
<ElFormItem v-if="createForm.flow_type === 'direct'" label="是否迁移数据">
|
||||
<ElSwitch v-model="createForm.migrate_data" />
|
||||
<div style="margin-top: 4px; margin-left: 8px; font-size: 12px; color: #909399">
|
||||
开启后将迁移钱包余额、套餐记录到新资产
|
||||
</div>
|
||||
</ElFormItem>
|
||||
<ElFormItem label="备注">
|
||||
<ElInput
|
||||
v-model="createForm.remark"
|
||||
@@ -257,6 +344,7 @@
|
||||
// 搜索表单
|
||||
const searchForm = reactive({
|
||||
status: undefined,
|
||||
flow_type: undefined, // 流程类型筛选
|
||||
identifier: '',
|
||||
created_at_range: [],
|
||||
created_at_start: '',
|
||||
@@ -269,6 +357,9 @@
|
||||
exchange_reason_other: '',
|
||||
old_asset_type: '',
|
||||
old_identifier: '',
|
||||
flow_type: 'shipping', // 流程类型,默认物流换货
|
||||
new_identifier: '', // 新资产标识符(direct 时必填)
|
||||
migrate_data: false, // 是否迁移数据(默认 false)
|
||||
remark: ''
|
||||
})
|
||||
|
||||
@@ -286,11 +377,27 @@
|
||||
callback()
|
||||
}
|
||||
|
||||
const validateNewIdentifier = (_rule: any, value: string, callback: any) => {
|
||||
if (createForm.flow_type !== 'direct') {
|
||||
callback()
|
||||
return
|
||||
}
|
||||
|
||||
if (!value || !value.trim()) {
|
||||
callback(new Error('请输入新资产标识符'))
|
||||
return
|
||||
}
|
||||
|
||||
callback()
|
||||
}
|
||||
|
||||
const createRules = reactive<FormRules>({
|
||||
exchange_reason: [{ required: true, message: '请选择换货原因', trigger: 'change' }],
|
||||
exchange_reason_other: [{ validator: validateOtherExchangeReason, trigger: 'blur' }],
|
||||
old_asset_type: [{ required: true, message: '请选择旧资产类型', trigger: 'change' }],
|
||||
old_identifier: [{ required: true, message: '请选择旧资产标识符', trigger: 'change' }]
|
||||
old_identifier: [{ required: true, message: '请选择旧资产标识符', trigger: 'change' }],
|
||||
flow_type: [{ required: true, message: '请选择流程类型', trigger: 'change' }],
|
||||
new_identifier: [{ validator: validateNewIdentifier, trigger: 'blur' }]
|
||||
})
|
||||
|
||||
// 旧资产搜索相关
|
||||
@@ -299,6 +406,12 @@
|
||||
const oldDeviceOptions = ref<Device[]>([])
|
||||
const oldDeviceSearchLoading = ref(false)
|
||||
|
||||
// 新资产搜索相关(直接换货使用)
|
||||
const newIotCardOptions = ref<StandaloneIotCard[]>([])
|
||||
const newCardSearchLoading = ref(false)
|
||||
const newDeviceOptions = ref<Device[]>([])
|
||||
const newDeviceSearchLoading = ref(false)
|
||||
|
||||
// 发货表单
|
||||
const shipForm = reactive({
|
||||
new_identifier: '',
|
||||
@@ -338,6 +451,19 @@
|
||||
{ label: '已取消', value: 5 }
|
||||
]
|
||||
},
|
||||
{
|
||||
label: '流程类型',
|
||||
prop: 'flow_type',
|
||||
type: 'select',
|
||||
config: {
|
||||
clearable: true,
|
||||
placeholder: '全部'
|
||||
},
|
||||
options: () => [
|
||||
{ label: '物流换货', value: 'shipping' },
|
||||
{ label: '直接换货', value: 'direct' }
|
||||
]
|
||||
},
|
||||
{
|
||||
label: '资产标识符',
|
||||
prop: 'identifier',
|
||||
@@ -384,6 +510,12 @@
|
||||
minWidth: 150,
|
||||
showOverflowTooltip: true
|
||||
},
|
||||
{
|
||||
prop: 'flow_type',
|
||||
label: '流程类型',
|
||||
width: 120,
|
||||
formatter: (row: any) => row.flow_type_name || (row.flow_type === 'direct' ? '直接换货' : '物流换货')
|
||||
},
|
||||
{
|
||||
prop: 'old_asset_type',
|
||||
label: '旧资产类型',
|
||||
@@ -474,6 +606,9 @@
|
||||
if (searchForm.status !== undefined && searchForm.status !== null) {
|
||||
params.status = searchForm.status
|
||||
}
|
||||
if (searchForm.flow_type) {
|
||||
params.flow_type = searchForm.flow_type
|
||||
}
|
||||
if (searchForm.identifier) {
|
||||
params.identifier = searchForm.identifier
|
||||
}
|
||||
@@ -547,6 +682,25 @@
|
||||
}
|
||||
}
|
||||
|
||||
// 流程类型变更
|
||||
const handleFlowTypeChange = async () => {
|
||||
// 清空新资产标识符和数据迁移选项
|
||||
createForm.new_identifier = ''
|
||||
createForm.migrate_data = false
|
||||
newIotCardOptions.value = []
|
||||
newDeviceOptions.value = []
|
||||
createFormRef.value?.clearValidate('new_identifier')
|
||||
|
||||
// 如果选择直接换货且已选择旧资产类型,加载默认新资产列表
|
||||
if (createForm.flow_type === 'direct' && createForm.old_asset_type) {
|
||||
if (createForm.old_asset_type === 'iot_card') {
|
||||
await loadDefaultNewIotCards()
|
||||
} else if (createForm.old_asset_type === 'device') {
|
||||
await loadDefaultNewDevices()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 旧资产类型变更
|
||||
const handleOldAssetTypeChange = async () => {
|
||||
// 清空旧资产标识符
|
||||
@@ -554,11 +708,26 @@
|
||||
oldIotCardOptions.value = []
|
||||
oldDeviceOptions.value = []
|
||||
|
||||
// 清空新资产标识符(如果是直接换货)
|
||||
if (createForm.flow_type === 'direct') {
|
||||
createForm.new_identifier = ''
|
||||
newIotCardOptions.value = []
|
||||
newDeviceOptions.value = []
|
||||
}
|
||||
|
||||
// 根据类型加载默认数据
|
||||
if (createForm.old_asset_type === 'iot_card') {
|
||||
await loadDefaultIotCards()
|
||||
// 如果是直接换货,也加载新资产列表
|
||||
if (createForm.flow_type === 'direct') {
|
||||
await loadDefaultNewIotCards()
|
||||
}
|
||||
} else if (createForm.old_asset_type === 'device') {
|
||||
await loadDefaultDevices()
|
||||
// 如果是直接换货,也加载新资产列表
|
||||
if (createForm.flow_type === 'direct') {
|
||||
await loadDefaultNewDevices()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -664,6 +833,102 @@
|
||||
}
|
||||
}
|
||||
|
||||
// 搜索新IoT卡(直接换货使用)
|
||||
const searchNewIotCards = async (query: string) => {
|
||||
if (!query) {
|
||||
// 清空搜索时加载默认列表
|
||||
await loadDefaultNewIotCards()
|
||||
return
|
||||
}
|
||||
|
||||
newCardSearchLoading.value = true
|
||||
try {
|
||||
const res = await CardService.getStandaloneIotCards({
|
||||
iccid: query,
|
||||
page: 1,
|
||||
page_size: 20
|
||||
})
|
||||
if (res.code === 0 && res.data) {
|
||||
newIotCardOptions.value = res.data.items || []
|
||||
if (newIotCardOptions.value.length === 0) {
|
||||
ElMessage.info('未找到匹配的IoT卡')
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('搜索IoT卡失败:', error)
|
||||
newIotCardOptions.value = []
|
||||
} finally {
|
||||
newCardSearchLoading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
// 加载默认新IoT卡列表
|
||||
const loadDefaultNewIotCards = async () => {
|
||||
newCardSearchLoading.value = true
|
||||
try {
|
||||
const res = await CardService.getStandaloneIotCards({
|
||||
page: 1,
|
||||
page_size: 10
|
||||
})
|
||||
if (res.code === 0 && res.data) {
|
||||
newIotCardOptions.value = res.data.items || []
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('加载IoT卡列表失败:', error)
|
||||
newIotCardOptions.value = []
|
||||
} finally {
|
||||
newCardSearchLoading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
// 搜索新设备(直接换货使用)
|
||||
const searchNewDevices = async (query: string) => {
|
||||
if (!query) {
|
||||
// 清空搜索时加载默认列表
|
||||
await loadDefaultNewDevices()
|
||||
return
|
||||
}
|
||||
|
||||
newDeviceSearchLoading.value = true
|
||||
try {
|
||||
const res = await DeviceService.getDevices({
|
||||
keyword: query,
|
||||
page: 1,
|
||||
page_size: 20
|
||||
})
|
||||
if (res.code === 0 && res.data) {
|
||||
newDeviceOptions.value = res.data.items || []
|
||||
if (newDeviceOptions.value.length === 0) {
|
||||
ElMessage.info('未找到匹配的设备')
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('搜索设备失败:', error)
|
||||
newDeviceOptions.value = []
|
||||
} finally {
|
||||
newDeviceSearchLoading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
// 加载默认新设备列表
|
||||
const loadDefaultNewDevices = async () => {
|
||||
newDeviceSearchLoading.value = true
|
||||
try {
|
||||
const res = await DeviceService.getDevices({
|
||||
page: 1,
|
||||
page_size: 10
|
||||
})
|
||||
if (res.code === 0 && res.data) {
|
||||
newDeviceOptions.value = res.data.items || []
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('加载设备列表失败:', error)
|
||||
newDeviceOptions.value = []
|
||||
} finally {
|
||||
newDeviceSearchLoading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
// 确认创建
|
||||
const handleConfirmCreate = () => {
|
||||
createFormRef.value?.validate(async (valid) => {
|
||||
@@ -676,12 +941,21 @@
|
||||
? createForm.exchange_reason_other.trim()
|
||||
: createForm.exchange_reason
|
||||
|
||||
const res = await ExchangeService.createExchange({
|
||||
const requestData: any = {
|
||||
exchange_reason: exchangeReason,
|
||||
old_asset_type: createForm.old_asset_type,
|
||||
old_identifier: createForm.old_identifier,
|
||||
flow_type: createForm.flow_type,
|
||||
remark: createForm.remark || undefined
|
||||
})
|
||||
}
|
||||
|
||||
// 直接换货时需要传递新资产标识符和数据迁移标志
|
||||
if (createForm.flow_type === 'direct') {
|
||||
requestData.new_identifier = createForm.new_identifier
|
||||
requestData.migrate_data = createForm.migrate_data
|
||||
}
|
||||
|
||||
const res = await ExchangeService.createExchange(requestData)
|
||||
if (res.code === 0) {
|
||||
ElMessage.success('换货单创建成功')
|
||||
createDialogVisible.value = false
|
||||
@@ -701,6 +975,8 @@
|
||||
// 清空搜索选项
|
||||
oldIotCardOptions.value = []
|
||||
oldDeviceOptions.value = []
|
||||
newIotCardOptions.value = []
|
||||
newDeviceOptions.value = []
|
||||
}
|
||||
|
||||
// 查看详情
|
||||
@@ -712,7 +988,22 @@
|
||||
const getActions = (row: ExchangeResponse) => {
|
||||
const actions: any[] = []
|
||||
const status = row.status
|
||||
const flowType = row.flow_type || 'shipping' // 历史数据兼容
|
||||
|
||||
// 直接换货不支持任何操作(发货、确认完成、取消)
|
||||
if (flowType === 'direct') {
|
||||
// 状态4(已完成):旧资产转新(direct 也支持)
|
||||
if (status === 4 && hasAuth('exchange:renew')) {
|
||||
actions.push({
|
||||
label: '旧资产转新',
|
||||
handler: () => handleRenewExchange(row),
|
||||
type: 'warning'
|
||||
})
|
||||
}
|
||||
return actions
|
||||
}
|
||||
|
||||
// 物流换货流程
|
||||
// 状态1(待填写信息):取消换货
|
||||
if (status === 1) {
|
||||
if (hasAuth('exchange:cancel')) {
|
||||
|
||||
@@ -968,7 +968,7 @@
|
||||
// 构建请求数据
|
||||
const data: any = {
|
||||
series_name: form.series_name,
|
||||
description: form.description || undefined,
|
||||
description: form.description,
|
||||
enable_one_time_commission: form.one_time_commission_config.enable
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user