feat(testReport): 新增测试报告分组和报告生成功能

- 添加ReportTaskState和ReportTaskGenerateState类型定义
- 扩展ReportTaskLedgerImportStage枚举,增加文件导入阶段
- 新增ReportTaskLedgerImportRequest、ReportTaskLedgerValidateAttachmentRecord等接口定义
- 添加validateReportTaskLedgerApi、importReportTaskLedgerApi等API方法
- 在操作列新增分组和生成报告按钮
- 实现ReportTaskGroupDialog组件用于监测点分组管理
- 添加分组报告下载功能和相关事件处理
- 优化报告任务详情对话框中的分组报告显示
- 更新createEmptyLedgerPreparedState函数初始化逻辑
This commit is contained in:
dk
2026-07-20 16:09:05 +08:00
parent 6984f66da4
commit 6d876ec7dd
8 changed files with 518 additions and 15 deletions

View File

@@ -37,11 +37,32 @@ export const downloadReportTaskLedgerTemplateApi = (): Promise<AxiosResponse<Blo
}) as unknown as Promise<AxiosResponse<Blob>>
export const validateReportTaskLedgerApi = (id: string, formData: FormData) => {
return http.post<ReportTask.ReportTaskLedgerImportResult>(`${REPORT_TASK_BASE_URL}/${id}/ledger/validate`, formData, {
return http.post<ReportTask.ReportTaskLedgerValidateResult>(`${REPORT_TASK_BASE_URL}/${id}/ledger/validate`, formData, {
headers: { 'Content-Type': 'multipart/form-data' }
})
}
export const importReportTaskLedgerApi = (id: string, params: ReportTask.ReportTaskLedgerImportRequest) => {
return http.post<ReportTask.ReportTaskLedgerImportResult>(`${REPORT_TASK_BASE_URL}/${id}/ledger/import`, params)
}
export const listReportTaskPointsApi = (id: string) => {
return http.get<ReportTask.ReportTaskPointRecord[]>(`${REPORT_TASK_BASE_URL}/${id}/point/list`)
}
export const saveReportTaskGroupsApi = (id: string, params: ReportTask.ReportTaskGroupSaveRequest) => {
return http.post<boolean>(`${REPORT_TASK_BASE_URL}/${id}/group/save`, params)
}
export const listReportTaskGroupReportsApi = (id: string) => {
return http.get<ReportTask.ReportTaskGroupReportRecord[]>(`${REPORT_TASK_BASE_URL}/${id}/group-report/list`)
}
export const generateReportTaskGroupReportsApi = (id: string) => {
return http.post<boolean>(`${REPORT_TASK_BASE_URL}/${id}/generate`)
}
export const downloadReportTaskGroupReportApi = (groupReportId: string): Promise<AxiosResponse<Blob>> =>
http.get(`${REPORT_TASK_BASE_URL}/group-report/${groupReportId}/download`, undefined, {
responseType: 'blob'
}) as unknown as Promise<AxiosResponse<Blob>>

View File

@@ -1,9 +1,12 @@
export namespace ReportTask {
export type ReportTaskState = '01' | '02' | '03' | '04' | '05'
export type ReportTaskGenerateState = 0 | 1 | 2
export type ReportTaskGroupReportGenerateState = 0 | 1 | 2 | 3
export type ReportTaskLedgerImportStage =
| '选择文件'
| '校验文件'
| '文件上传'
| '文件导入'
| '基础资料维护'
| '完成'
@@ -41,6 +44,7 @@ export namespace ReportTask {
searchBeginTime?: string
searchEndTime?: string
timeRange?: string[]
state?: ReportTaskState | string
pageNum?: number
pageSize?: number
}
@@ -65,12 +69,59 @@ export namespace ReportTask {
checkResult?: string
}
export interface ReportTaskLedgerImportRequest {
batchId: string
}
export interface ReportTaskLedgerImportStageRecord {
stage?: ReportTaskLedgerImportStage | string | null
success?: boolean | null
message?: string | null
}
export interface ReportTaskLedgerValidateAttachmentRecord {
attachmentName?: string | null
attachmentType?: string | null
referenced?: boolean | null
uploaded?: boolean | null
missing?: boolean | null
reuploadAllowed?: boolean | null
message?: string | null
}
export interface ReportTaskLedgerValidatePointRecord {
rowNo?: number | null
pointKey?: string | null
substationName?: string | null
pointName?: string | null
complete?: boolean | null
missingAttachmentCount?: number | null
testDeviceNoValid?: boolean | null
testDeviceNoMessage?: string | null
clientUnitNameValid?: boolean | null
clientUnitNameMessage?: string | null
attachments?: ReportTaskLedgerValidateAttachmentRecord[] | null
}
export interface ReportTaskLedgerValidateResult {
sessionId?: string | null
batchId?: string | null
success?: boolean | null
summaryFileName?: string | null
summaryFilePresent?: boolean | null
tempStoragePath?: string | null
uploadedFileCount?: number | null
pointCount?: number | null
missingAttachmentCount?: number | null
canContinueUpload?: boolean | null
requiredSheets?: string[] | null
existingSheets?: string[] | null
missingSheets?: string[] | null
orphanAttachmentNames?: string[] | null
failReasons?: string[] | null
points?: ReportTaskLedgerValidatePointRecord[] | null
}
export interface ReportTaskLedgerImportSnapshotGroupRecord {
groupNo?: number | null
count?: number | null
@@ -100,7 +151,9 @@ export namespace ReportTask {
currentStage?: ReportTaskLedgerImportStage | string | null
success?: boolean | null
summaryFileName?: string | null
tempStoragePath?: string | null
totalFileCount?: number
uploadedFileCount?: number
pointCount?: number
excelAttachmentCount?: number
wordAttachmentCount?: number
@@ -153,9 +206,13 @@ export namespace ReportTask {
export interface ReportTaskLedgerPreparedState {
draftId: string
batchId: string
validateSessionId: string
validateResult: ReportTaskLedgerValidateResult | null
latestImportResult: ReportTaskLedgerImportResult | null
snapshot: ReportTaskLedgerImportSnapshot | null
hasImported: boolean
validatedBatchId: string
pendingReimport: boolean
pendingRevalidate: boolean
}
}

View File

@@ -44,6 +44,19 @@
</el-table-column>
<el-table-column prop="generateMessage" label="生成消息" min-width="180" show-overflow-tooltip />
<el-table-column prop="generateTime" label="生成时间" min-width="160" show-overflow-tooltip />
<el-table-column label="操作" width="120" fixed="right">
<template #default="{ row }">
<el-button
link
type="primary"
:disabled="row.generateState !== 2 || !row.id"
:loading="downloadingGroupReportId === row.id"
@click="emit('download-group-report', row)"
>
下载
</el-button>
</template>
</el-table-column>
</el-table>
</div>
</div>
@@ -75,10 +88,12 @@ const props = defineProps<{
groupReports: ReportTask.ReportTaskGroupReportRecord[]
companyLabelMap: Record<string, string>
standardLabelMap: Record<string, string>
downloadingGroupReportId?: string | null
}>()
const emit = defineEmits<{
(event: 'update:visible', value: boolean): void
(event: 'download-group-report', value: ReportTask.ReportTaskGroupReportRecord): void
}>()
const visibleProxy = computed({
@@ -89,6 +104,7 @@ const visibleProxy = computed({
const companyLabelMap = computed(() => props.companyLabelMap)
const standardLabelMap = computed(() => props.standardLabelMap)
const groupReports = computed(() => props.groupReports || [])
const downloadingGroupReportId = computed(() => props.downloadingGroupReportId || '')
</script>
<style scoped lang="scss">

View File

@@ -321,10 +321,14 @@ const logEntries = ref<Array<{ id: string; type: ReportTaskLogType; time: string
const ledgerPreparedState = ref<ReportTask.ReportTaskLedgerPreparedState>({
draftId: '',
batchId: '',
validateSessionId: '',
validateResult: null,
latestImportResult: null,
snapshot: null,
hasImported: false,
pendingReimport: false
validatedBatchId: '',
pendingReimport: false,
pendingRevalidate: false
})
const lastHandledSaveSuccessVersion = ref(0)
@@ -445,10 +449,14 @@ const resetForm = () => {
ledgerPreparedState.value = {
draftId: '',
batchId: '',
validateSessionId: '',
validateResult: null,
latestImportResult: null,
snapshot: null,
hasImported: false,
pendingReimport: false
validatedBatchId: '',
pendingReimport: false,
pendingRevalidate: false
}
}
@@ -494,10 +502,14 @@ const fillForm = async () => {
ledgerPreparedState.value = {
draftId: activeDraftId.value,
batchId: '',
validateSessionId: '',
validateResult: null,
latestImportResult: null,
snapshot,
hasImported: Boolean(snapshot || Number(record?.pointCount) > 0),
pendingReimport: false
validatedBatchId: '',
pendingReimport: false,
pendingRevalidate: false
}
applyInitialStep(Boolean(snapshot || Number(record?.pointCount) > 0))

View File

@@ -0,0 +1,234 @@
<template>
<el-dialog v-model="visibleProxy" title="监测点分组" width="1120px" append-to-body destroy-on-close>
<div v-loading="loading" class="report-task-group-dialog">
<el-descriptions :column="4" border class="summary-descriptions">
<el-descriptions-item label="报告编号">{{ resolveReportTaskText(record?.no) }}</el-descriptions-item>
<el-descriptions-item label="委托单位">{{ resolveReportTaskText(record?.clientUnitName) }}</el-descriptions-item>
<el-descriptions-item label="监测点数">{{ pointList.length || 0 }}</el-descriptions-item>
<el-descriptions-item label="已分组点位">{{ groupedPointCount }}</el-descriptions-item>
<el-descriptions-item label="分组数量">{{ groupedPayload.groups.length || 0 }}</el-descriptions-item>
</el-descriptions>
<el-empty v-if="!loading && !pointList.length" description="暂无监测点数据,无法进行分组" :image-size="88" />
<template v-else>
<el-alert
v-if="!groupingCompleted"
title="请为所有监测点设置大于 0 的分组号后再保存。"
type="warning"
:closable="false"
show-icon
class="section-alert"
/>
<div class="toolbar">
<div class="toolbar-title">批量分组</div>
<div class="toolbar-actions">
<el-input-number
v-model="batchGroupNo"
:min="1"
:step="1"
controls-position="right"
placeholder="分组号"
/>
<el-button type="primary" :disabled="!selectedPointIds.length || !batchGroupNo" @click="applyBatchGroupNo">
应用到已选
</el-button>
<el-button :disabled="!selectedPointIds.length" @click="clearSelectedGroups">清空已选</el-button>
</div>
</div>
<el-table
:data="pointList"
row-key="id"
border
size="small"
max-height="460"
class="point-table"
@selection-change="handleSelectionChange"
>
<el-table-column type="selection" width="55" />
<el-table-column type="index" label="序号" width="70" />
<el-table-column label="分组号" width="120">
<template #default="{ row }">
<el-input-number
:model-value="resolvePointGroupNo(row.id)"
:min="1"
:step="1"
controls-position="right"
@update:model-value="handlePointGroupNoChange(row.id, $event)"
/>
</template>
</el-table-column>
<el-table-column prop="substationName" label="变电站" min-width="180" show-overflow-tooltip />
<el-table-column prop="pointName" label="监测点" min-width="180" show-overflow-tooltip />
<el-table-column prop="monitorTime" label="监测时间" min-width="160" show-overflow-tooltip />
<el-table-column prop="voltageLevel" label="电压等级" min-width="120" show-overflow-tooltip />
<el-table-column prop="excelAttachmentName" label="原始文件" min-width="180" show-overflow-tooltip />
</el-table>
</template>
</div>
<template #footer>
<el-button @click="visibleProxy = false">取消</el-button>
<el-button type="primary" :loading="saving" :disabled="loading || !groupingCompleted" @click="handleSubmit">
保存分组
</el-button>
</template>
</el-dialog>
</template>
<script setup lang="ts">
import { computed, ref, watch } from 'vue'
import type { ReportTask } from '@/api/aireport/testReport/interface'
import {
checkReportTaskGroupingCompleted,
normalizeReportTaskGroupSavePayload,
resolveReportTaskText
} from '../utils/testReport'
defineOptions({
name: 'ReportTaskGroupDialog'
})
const props = defineProps<{
visible: boolean
loading: boolean
saving: boolean
record: ReportTask.ReportTaskRecord | null
pointList: ReportTask.ReportTaskPointRecord[]
}>()
const emit = defineEmits<{
(event: 'update:visible', value: boolean): void
(event: 'submit', value: ReportTask.ReportTaskGroupSaveRequest): void
}>()
const localPointGroupMap = ref<Record<string, number | null>>({})
const selectedPointIds = ref<string[]>([])
const batchGroupNo = ref<number | null>(null)
const visibleProxy = computed({
get: () => props.visible,
set: value => emit('update:visible', value)
})
const initializePointGroupMap = () => {
const nextGroupMap: Record<string, number | null> = {}
props.pointList.forEach(point => {
const pointId = String(point.id || '').trim()
if (!pointId) return
const groupNo = Number(point.groupNo)
nextGroupMap[pointId] = Number.isInteger(groupNo) && groupNo > 0 ? groupNo : null
})
localPointGroupMap.value = nextGroupMap
selectedPointIds.value = []
batchGroupNo.value = null
}
watch(
() => [props.visible, props.pointList],
([visible]) => {
if (visible) {
initializePointGroupMap()
}
},
{ immediate: true, deep: true }
)
const groupedPayload = computed(() => normalizeReportTaskGroupSavePayload(props.pointList, localPointGroupMap.value))
const groupingCompleted = computed(() => checkReportTaskGroupingCompleted(props.pointList, groupedPayload.value))
const groupedPointCount = computed(() =>
props.pointList.reduce((count, point) => {
const pointId = String(point.id || '').trim()
if (!pointId) return count
const groupNo = Number(localPointGroupMap.value[pointId])
return Number.isInteger(groupNo) && groupNo > 0 ? count + 1 : count
}, 0)
)
const resolvePointGroupNo = (pointId?: string) => {
const normalizedPointId = String(pointId || '').trim()
if (!normalizedPointId) return null
return localPointGroupMap.value[normalizedPointId] ?? null
}
const updatePointGroupNo = (pointId: string | undefined, value: number | null | undefined) => {
const normalizedPointId = String(pointId || '').trim()
if (!normalizedPointId) return
const groupNo = Number(value)
localPointGroupMap.value = {
...localPointGroupMap.value,
[normalizedPointId]: Number.isInteger(groupNo) && groupNo > 0 ? groupNo : null
}
}
const handlePointGroupNoChange = (pointId: string | undefined, value: number | null | undefined) => {
updatePointGroupNo(pointId, value)
}
const handleSelectionChange = (selection: ReportTask.ReportTaskPointRecord[]) => {
selectedPointIds.value = selection.map(item => String(item.id || '').trim()).filter(Boolean)
}
const applyBatchGroupNo = () => {
const groupNo = Number(batchGroupNo.value)
if (!Number.isInteger(groupNo) || groupNo <= 0) return
const nextGroupMap = { ...localPointGroupMap.value }
selectedPointIds.value.forEach(pointId => {
nextGroupMap[pointId] = groupNo
})
localPointGroupMap.value = nextGroupMap
}
const clearSelectedGroups = () => {
const nextGroupMap = { ...localPointGroupMap.value }
selectedPointIds.value.forEach(pointId => {
nextGroupMap[pointId] = null
})
localPointGroupMap.value = nextGroupMap
}
const handleSubmit = () => {
emit('submit', groupedPayload.value)
}
</script>
<style scoped lang="scss">
.report-task-group-dialog {
min-height: 180px;
}
.summary-descriptions,
.section-alert,
.point-table {
margin-top: 16px;
}
.toolbar {
display: flex;
align-items: center;
justify-content: space-between;
gap: 12px;
margin-top: 16px;
flex-wrap: wrap;
}
.toolbar-title {
font-size: 15px;
font-weight: 600;
color: var(--el-text-color-primary);
}
.toolbar-actions {
display: flex;
align-items: center;
gap: 12px;
flex-wrap: wrap;
}
</style>

View File

@@ -24,6 +24,17 @@
</template>
<template #operation="{ row }">
<el-button link type="primary" :icon="View" @click="openDetailDialog(row)">详情</el-button>
<el-button link type="primary" :disabled="!isReportTaskGeneratePending(row) || !row.pointCount" @click="openGroupDialog(row)">
分组
</el-button>
<el-button
link
type="primary"
:disabled="!isReportTaskGeneratePending(row) || !row.groupCount"
@click="handleGenerateGroupReports(row)"
>
生成报告
</el-button>
<el-button link type="primary" :icon="Edit" @click="openEditDialog(row)">编辑</el-button>
<el-button link type="primary" :icon="Upload" @click="openAuditDialog(row)">提交审核</el-button>
<el-button link type="danger" :icon="Delete" @click="handleDelete(row)">删除</el-button>
@@ -50,6 +61,8 @@
:group-reports="detailGroupReports"
:company-label-map="companyLabelMap"
:standard-label-map="standardLabelMap"
:downloading-group-report-id="downloadingGroupReportId"
@download-group-report="handleDownloadGroupReport"
/>
<ReportTaskAuditDialog
@@ -58,6 +71,15 @@
:record="currentAuditRecord"
@submit="handleSubmitAudit"
/>
<ReportTaskGroupDialog
v-model:visible="groupDialogVisible"
:loading="groupDialogLoading"
:saving="groupDialogSaving"
:record="currentGroupRecord"
:point-list="groupPointList"
@submit="handleSaveGroups"
/>
</div>
</template>
@@ -71,10 +93,14 @@ import type { ColumnProps, ProTableInstance } from '@/components/ProTable/interf
import {
createReportTaskApi,
deleteReportTasksApi,
downloadReportTaskGroupReportApi,
exportReportTasksApi,
generateReportTaskGroupReportsApi,
getReportTaskDetailApi,
listReportTaskGroupReportsApi,
listReportTaskPointsApi,
listReportTasksApi,
saveReportTaskGroupsApi,
submitReportTaskAuditApi,
updateReportTaskApi
} from '@/api/aireport/testReport'
@@ -88,6 +114,7 @@ import { useDictStore } from '@/stores/modules/dict'
import ReportTaskAuditDialog from './components/ReportTaskAuditDialog.vue'
import ReportTaskDetailDialog from './components/ReportTaskDetailDialog.vue'
import ReportTaskFormDialog from './components/ReportTaskFormDialog.vue'
import ReportTaskGroupDialog from './components/ReportTaskGroupDialog.vue'
import {
buildDictSelectOptions,
buildOptionLabelMap,
@@ -95,6 +122,7 @@ import {
normalizeReportTaskGroupReportList,
normalizeReportTaskListParams,
normalizeReportTaskPage,
normalizeReportTaskPointList,
resolveReportTaskCreateUnitText,
resolveReportTaskStandardText,
resolveReportTaskText,
@@ -110,17 +138,23 @@ const proTable = ref<ProTableInstance>()
const formDialogVisible = ref(false)
const detailDialogVisible = ref(false)
const auditDialogVisible = ref(false)
const groupDialogVisible = ref(false)
const formSaving = ref(false)
const formSaveSuccessVersion = ref(0)
const detailLoading = ref(false)
const auditSaving = ref(false)
const groupDialogLoading = ref(false)
const groupDialogSaving = ref(false)
const formMode = ref<'create' | 'edit'>('create')
const currentRecord = ref<ReportTask.ReportTaskRecord | null>(null)
const detailRecord = ref<ReportTask.ReportTaskRecord | null>(null)
const detailGroupReports = ref<ReportTask.ReportTaskGroupReportRecord[]>([])
const currentAuditRecord = ref<ReportTask.ReportTaskRecord | null>(null)
const currentGroupRecord = ref<ReportTask.ReportTaskRecord | null>(null)
const groupPointList = ref<ReportTask.ReportTaskPointRecord[]>([])
const clientUnitOptions = ref<Array<{ label: string; value: string }>>([])
const reportModelOptions = ref<Array<{ label: string; value: string }>>([])
const downloadingGroupReportId = ref('')
const companyOptions = computed(() => buildDictSelectOptions(dictStore.getDictData(DICT_CODES.TEST_REPORT_COMPANY)))
const standardOptions = computed(() => buildDictSelectOptions(dictStore.getDictData(DICT_CODES.TEST_REPORT_STANDARD)))
@@ -130,6 +164,7 @@ const requiredDictCodes = [DICT_CODES.TEST_REPORT_COMPANY, DICT_CODES.TEST_REPOR
const currentSearchParams = computed(() =>
normalizeReportTaskListParams((proTable.value?.searchParam || {}) as ReportTask.ReportTaskListParams)
)
const isReportTaskGeneratePending = (row: ReportTask.ReportTaskRecord) => Number(row.reportGenerateState ?? 0) === 0
const columns = reactive<ColumnProps<ReportTask.ReportTaskRecord>[]>([
{ type: 'selection', fixed: 'left', width: 70 },
@@ -191,7 +226,7 @@ const columns = reactive<ColumnProps<ReportTask.ReportTaskRecord>[]>([
{ prop: 'createTime', label: '创建时间', minWidth: 170, render: ({ row }) => resolveReportTaskText(row.createTime) },
{ prop: 'updateByName', label: '更新人', minWidth: 120, isShow: false, render: ({ row }) => resolveReportTaskText(row.updateByName || row.updateBy) },
{ prop: 'updateTime', label: '更新时间', minWidth: 170, isShow: false, render: ({ row }) => resolveReportTaskText(row.updateTime) },
{ prop: 'operation', label: '操作', fixed: 'right', width: 280 }
{ prop: 'operation', label: '操作', fixed: 'right', width: 360 }
])
const ensureReportTaskDictOptionsReady = async () => {
@@ -297,6 +332,27 @@ const openEditDialog = async (row: ReportTask.ReportTaskRecord) => {
}
}
const loadDetailDialogData = async (id: string) => {
const [detailResponse, groupReportsResponse] = await Promise.all([getReportTaskDetailApi(id), listReportTaskGroupReportsApi(id)])
detailRecord.value = unwrapReportTaskPayload<ReportTask.ReportTaskRecord>(detailResponse)
detailGroupReports.value = normalizeReportTaskGroupReportList(groupReportsResponse)
}
const refreshDetailDialogDataIfMatched = async (id: string) => {
if (!detailDialogVisible.value) return
const currentDetailId = String(detailRecord.value?.id || '').trim()
if (currentDetailId !== id) return
detailLoading.value = true
try {
await loadDetailDialogData(id)
} finally {
detailLoading.value = false
}
}
const openDetailDialog = async (row: ReportTask.ReportTaskRecord) => {
if (!row.id) {
ElMessage.warning('当前报告任务缺少 ID无法查看详情')
@@ -309,13 +365,7 @@ const openDetailDialog = async (row: ReportTask.ReportTaskRecord) => {
detailLoading.value = true
try {
const [detailResponse, groupReportsResponse] = await Promise.all([
getReportTaskDetailApi(row.id),
listReportTaskGroupReportsApi(row.id)
])
detailRecord.value = unwrapReportTaskPayload<ReportTask.ReportTaskRecord>(detailResponse)
detailGroupReports.value = normalizeReportTaskGroupReportList(groupReportsResponse)
await loadDetailDialogData(row.id)
} catch (error) {
ElMessage.error(getReportTaskErrorMessage(error, '报告任务详情查询失败'))
} finally {
@@ -323,6 +373,33 @@ const openDetailDialog = async (row: ReportTask.ReportTaskRecord) => {
}
}
const openGroupDialog = async (row: ReportTask.ReportTaskRecord) => {
if (!row.id) {
ElMessage.warning('当前报告任务缺少 ID无法维护分组')
return
}
if (!isReportTaskGeneratePending(row)) {
ElMessage.warning('当前报告任务已生成过报告,不能再修改分组')
return
}
currentGroupRecord.value = row
groupPointList.value = []
groupDialogVisible.value = true
groupDialogLoading.value = true
try {
const response = await listReportTaskPointsApi(row.id)
groupPointList.value = normalizeReportTaskPointList(response)
} catch (error) {
groupDialogVisible.value = false
ElMessage.error(getReportTaskErrorMessage(error, '监测点列表查询失败'))
} finally {
groupDialogLoading.value = false
}
}
const openAuditDialog = (row: ReportTask.ReportTaskRecord) => {
if (!row.id) {
ElMessage.warning('当前报告任务缺少 ID无法提交审核')
@@ -368,6 +445,86 @@ const handleSubmitAudit = async (params: ReportTask.ReportTaskAuditRequest) => {
}
}
const handleSaveGroups = async (params: ReportTask.ReportTaskGroupSaveRequest) => {
const reportId = String(currentGroupRecord.value?.id || '').trim()
if (!reportId) {
ElMessage.warning('当前报告任务缺少 ID无法保存分组')
return
}
groupDialogSaving.value = true
try {
await saveReportTaskGroupsApi(reportId, params)
ElMessage.success('监测点分组保存成功')
groupDialogVisible.value = false
refreshReportTasks()
await refreshDetailDialogDataIfMatched(reportId)
} catch (error) {
ElMessage.error(getReportTaskErrorMessage(error, '监测点分组保存失败'))
} finally {
groupDialogSaving.value = false
}
}
const handleGenerateGroupReports = async (row: ReportTask.ReportTaskRecord) => {
if (!row.id) {
ElMessage.warning('当前报告任务缺少 ID无法生成报告')
return
}
if (!isReportTaskGeneratePending(row)) {
ElMessage.warning('当前报告任务不是待生成状态,不能重复生成')
return
}
if (!row.groupCount) {
ElMessage.warning('请先完成监测点分组后再生成报告')
return
}
try {
await ElMessageBox.confirm('将按当前分组生成所有报告,是否继续?', '生成报告', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
})
} catch {
return
}
try {
await generateReportTaskGroupReportsApi(row.id)
ElMessage.success('报告生成任务已执行完成')
refreshReportTasks()
await refreshDetailDialogDataIfMatched(row.id)
} catch (error) {
ElMessage.error(getReportTaskErrorMessage(error, '报告生成失败'))
}
}
const handleDownloadGroupReport = async (row: ReportTask.ReportTaskGroupReportRecord) => {
const groupReportId = String(row.id || '').trim()
if (!groupReportId) {
ElMessage.warning('当前分组报告缺少 ID无法下载')
return
}
downloadingGroupReportId.value = groupReportId
try {
await useDownloadWithServerFileName(
() => downloadReportTaskGroupReportApi(groupReportId),
row.reportFileName || row.reportName || `分组报告_${row.groupNo || ''}`,
undefined,
false,
'.docx'
)
} catch (error) {
ElMessage.error(getReportTaskErrorMessage(error, '分组报告下载失败'))
} finally {
downloadingGroupReportId.value = ''
}
}
const deleteReportTasks = async (ids: string[], successMessage: string) => {
if (!ids.length) {
ElMessage.warning('请选择要删除的报告任务')

View File

@@ -39,12 +39,14 @@ export const REPORT_TASK_FLOW_STEPS: ReportTaskFlowStepOption[] = [
export const createEmptyLedgerPreparedState = (): ReportTask.ReportTaskLedgerPreparedState => ({
draftId: '',
batchId: '',
validateSessionId: '',
validateResult: null,
latestImportResult: null,
snapshot: null,
hasImported: false,
validatedBatchId: '',
pendingReimport: false,
pendingRevalidate: false
})

View File

@@ -145,9 +145,13 @@ export const normalizeReportTaskPointList = (
export const normalizeReportTaskLedgerImportResult = (
response:
ResultData<ReportTask.ReportTaskLedgerImportResult> | ReportTask.ReportTaskLedgerImportResult | null | undefined
| ResultData<ReportTask.ReportTaskLedgerValidateResult>
| ReportTask.ReportTaskLedgerValidateResult
): ReportTask.ReportTaskLedgerImportResult => {
const payload = unwrapReportTaskPayload<ReportTask.ReportTaskLedgerImportResult | null | undefined>(
response as ResultData<ReportTask.ReportTaskLedgerImportResult>
const payload = unwrapReportTaskPayload<
Partial<ReportTask.ReportTaskLedgerImportResult & ReportTask.ReportTaskLedgerValidateResult> | null | undefined
>(
response as ResultData<Partial<ReportTask.ReportTaskLedgerImportResult & ReportTask.ReportTaskLedgerValidateResult>>
)
const stageList = Array.isArray(payload?.stages) ? payload.stages : []
@@ -169,7 +173,7 @@ export const normalizeReportTaskLedgerImportResult = (
clientReuseCount: payload?.clientReuseCount ?? 0,
groupCount: payload?.groupCount ?? 0,
failReasons: Array.isArray(payload?.failReasons) ? payload.failReasons.filter(Boolean) : [],
stages: stageList.map(stage => ({
stages: stageList.map((stage: ReportTask.ReportTaskLedgerImportStageRecord) => ({
stage: stage?.stage || undefined,
success: stage?.success ?? false,
message: stage?.message || ''