Files
one-pipe-system/src/components/business/traffic-records/RefundPostProcessAttempts.vue
邹进 8b01169219
Some checks failed
构建并部署前端到测试环境 / deploy (push) Failing after 4m30s
fix: 记录查询筛选改为按名称选择,去掉需要手填内部 ID 的条件
- 套餐日流量详单按套餐名称搜索选择,操作人按账号用户名搜索选择
- 去掉套餐使用记录ID、退款单ID 等手填 ID 的筛选与列;从退款详情进入时仍按该退款单过滤
2026-11-07 17:10:16 +09:00

205 lines
7.0 KiB
Vue

<template>
<div class="traffic-records">
<ElAlert
v-if="!allowed"
type="warning"
title="退款资产后处理尝试记录仅平台账号可查看"
:closable="false"
show-icon
/>
<template v-else>
<ArtSearchBar
:filter="filters"
:items="searchItems"
:show-expand="true"
label-width="110px"
@update:filter="(value: Record<string, unknown>) => Object.assign(filters, value)"
@search="search"
@reset="reset"
/>
<ElCard shadow="never">
<RecordTableShell
:error="error"
hint="时间范围必填,跨度不超过 31 天,结束时间不含;按时间倒序。"
:page="pagination.page"
:page-size="pagination.page_size"
:total="total"
@page-change="changePage"
@size-change="changeSize"
>
<ElTable v-loading="loading" :data="rows" row-key="id" border empty-text="暂无尝试记录">
<ElTableColumn label="记录时间" min-width="170">
<template #default="{ row }">{{ formatDateTime(row.created_at) }}</template>
</ElTableColumn>
<ElTableColumn label="退款单号" min-width="180">
<template #default="{ row }">{{ row.refund_no || row.refund_id }}</template>
</ElTableColumn>
<ElTableColumn label="资产" min-width="200">
<template #default="{ row }">
{{ row.asset_type === 'device' ? '设备' : '单卡' }}
{{
row.asset_type === 'device'
? assetDisplay({
device_id: row.asset_id,
device_virtual_no: row.device_virtual_no,
device_imei: row.device_imei
})
: assetDisplay({ iot_card_id: row.asset_id, iccid: row.iccid })
}}
</template>
</ElTableColumn>
<ElTableColumn label="第几次" prop="attempt" width="80" />
<ElTableColumn label="步骤" width="110">
<template #default="{ row }">{{
labelOf(REFUND_POST_STEP_LABELS, row.step)
}}</template>
</ElTableColumn>
<ElTableColumn label="结果" width="80">
<template #default="{ row }">
<ElTag :type="row.result === 'success' ? 'success' : 'danger'" size="small">
{{ labelOf(REFUND_POST_RESULT_LABELS, row.result) }}
</ElTag>
</template>
</ElTableColumn>
<ElTableColumn label="错误码" prop="error_code" min-width="120" show-overflow-tooltip />
<ElTableColumn
label="错误信息"
prop="error_message"
min-width="220"
show-overflow-tooltip
/>
<ElTableColumn label="触发方式" width="90">
<template #default="{ row }">{{
labelOf(REFUND_POST_TRIGGER_LABELS, row.trigger)
}}</template>
</ElTableColumn>
<ElTableColumn label="耗时(ms)" prop="duration_ms" width="90" />
</ElTable>
</RecordTableShell>
</ElCard>
</template>
</div>
</template>
<script setup lang="ts">
import { computed, reactive, watch } from 'vue'
import { storeToRefs } from 'pinia'
import { TrafficRecordsService } from '@/api/modules/trafficRecords'
import { useRecordList } from '@/composables/useRecordList'
import { useUserStore } from '@/store/modules/user'
import type { SearchFormItem } from '@/types'
import type { RefundPostProcessAttempt } from '@/types/api/trafficRecords'
import { formatDateTime } from '@/utils/business/format'
import {
REFUND_POST_RESULT_LABELS,
REFUND_POST_RESULT_OPTIONS,
REFUND_POST_STEP_LABELS,
REFUND_POST_STEP_OPTIONS,
REFUND_POST_TRIGGER_LABELS,
REFUND_POST_TRIGGER_OPTIONS,
RECORD_MAX_SPAN_DAYS,
labelOf
} from '@/config/constants/trafficRecords'
import {
compactQuery,
assetDisplay,
isPlatformAccount,
recentTimeRange,
toTimeQuery
} from '@/utils/business/trafficRecords'
import RecordTableShell from './RecordTableShell.vue'
/** refundId 来自退款详情入口:自动按该退款单筛选,时间范围取最近 31 天 */
const props = defineProps<{ refundId?: number }>()
const { info: userInfo } = storeToRefs(useUserStore())
const allowed = computed(() => isPlatformAccount(userInfo.value.user_type))
const emptyFilters = () => ({
refund_no: '',
identifier: '',
step: '',
result: '',
error_code: '',
trigger: '',
range: recentTimeRange(props.refundId ? RECORD_MAX_SPAN_DAYS : 7) as [string, string] | null
})
const filters = reactive(emptyFilters())
const { rows, total, loading, error, pagination, load, search, changePage, changeSize, clear } =
useRecordList<RefundPostProcessAttempt>((page) =>
TrafficRecordsService.getRefundPostProcessAttempts(
compactQuery({
...page,
...toTimeQuery(filters.range, true),
// 从退款详情进入时固定按该退款单筛选,不让用户手填内部 ID
refund_id: props.refundId || undefined,
refund_no: filters.refund_no.trim(),
identifier: filters.identifier.trim(),
step: filters.step,
result: filters.result,
error_code: filters.error_code.trim(),
trigger: filters.trigger
})
)
)
const select = (label: string, prop: string, options: SearchFormItem['options']) => ({
label,
prop,
type: 'select' as const,
options,
config: { clearable: true }
})
const searchItems: SearchFormItem[] = [
{
label: '记录时间',
prop: 'range',
type: 'datetimerange',
config: {
type: 'datetimerange',
rangeSeparator: '至',
startPlaceholder: '开始时间',
endPlaceholder: '结束时间',
valueFormat: 'YYYY-MM-DD HH:mm:ss',
clearable: true
}
},
{ label: '退款单号', prop: 'refund_no', type: 'input', config: { clearable: true } },
{
label: '资产标识',
prop: 'identifier',
type: 'input',
config: { clearable: true, maxlength: 64, placeholder: 'ICCID / 虚拟号 / IMEI / SN' }
},
select('处理步骤', 'step', REFUND_POST_STEP_OPTIONS),
select('结果', 'result', REFUND_POST_RESULT_OPTIONS),
{ label: '错误码', prop: 'error_code', type: 'input', config: { clearable: true } },
select('触发方式', 'trigger', REFUND_POST_TRIGGER_OPTIONS)
]
function reset() {
Object.assign(filters, emptyFilters())
void search()
}
watch(
() => [props.refundId, allowed.value],
() => {
clear()
Object.assign(filters, emptyFilters())
pagination.page = 1
if (allowed.value) void load()
},
{ immediate: true }
)
</script>
<style scoped lang="scss">
.traffic-records {
display: flex;
flex-direction: column;
gap: 16px;
min-width: 0;
}
</style>