refactor(aireport): 重构报告任务模块数据结构和界面组件

- 扩展 ReportTaskGenerateState 类型定义,增加状态值 3 和 4
- 移除 ReportTaskRecord 中的 clientUnitId、clientUnitName、createUnit、contractNumber、standard、phonenumber 字段
- 新增 multiId、templateDirName、templateDirPath、sortNo 等字段到 ReportTaskRecord
- 更新 ReportTaskAddParam 接口,移除 clientUnitId、createUnit、contractNumber、standard、phonenumber 参数
- 在 PointGroupItem 和 GroupReportRecord 中增加 reportNo、clientUnitId、createUnit、contractNumber、standard、phonenumber 字段
- 移除委托单位和检测公司等字段的显示列和表单验证规则
- 更新基础信息步骤组件,简化表单字段配置
- 调整详情对话框布局,增加分组报告表格中的委托单位、检测公司等列
- 优化分组数据显示逻辑,支持从快照数据获取分组数量
- 更新多报告界面组件,同步调整相关属性传递和数据加载逻辑
This commit is contained in:
dk
2026-07-22 11:36:31 +08:00
parent ed81e58583
commit 5b0088ee7b
12 changed files with 433 additions and 559 deletions

View File

@@ -1,6 +1,6 @@
export namespace ReportTask { export namespace ReportTask {
export type ReportTaskState = '01' | '02' | '03' | '04' | '05' export type ReportTaskState = '01' | '02' | '03' | '04' | '05'
export type ReportTaskGenerateState = 0 | 1 | 2 export type ReportTaskGenerateState = 0 | 1 | 2 | 3 | 4
export type ReportTaskGroupReportGenerateState = 0 | 1 | 2 | 3 export type ReportTaskGroupReportGenerateState = 0 | 1 | 2 | 3
export type ReportTaskLedgerImportStage = export type ReportTaskLedgerImportStage =
| '选择文件' | '选择文件'
@@ -12,16 +12,14 @@ export namespace ReportTask {
export interface ReportTaskRecord { export interface ReportTaskRecord {
id?: string id?: string
multiId?: string | null
no?: string | null no?: string | null
clientUnitId?: string | null
clientUnitName?: string | null
reportModelId?: string | null reportModelId?: string | null
reportModelName?: string | null reportModelName?: string | null
createUnit?: string | null templateDirName?: string | null
contractNumber?: string | null templateDirPath?: string | null
standard?: string[] | string | null sortNo?: number | null
data?: string | null data?: Record<string, unknown> | unknown[] | string | null
phonenumber?: string | null
checkId?: string | null checkId?: string | null
checkerName?: string | null checkerName?: string | null
checkTime?: string | null checkTime?: string | null
@@ -53,14 +51,9 @@ export namespace ReportTask {
export interface ReportTaskAddParam { export interface ReportTaskAddParam {
id?: string id?: string
no: string no: string
clientUnitId: string
reportModelId: string reportModelId: string
createUnit: string
contractNumber: string
standard: string[]
data: Record<string, unknown> | unknown[] data: Record<string, unknown> | unknown[]
batchId?: string batchId?: string
phonenumber?: string
} }
export interface ReportTaskSaveRequest extends ReportTaskAddParam {} export interface ReportTaskSaveRequest extends ReportTaskAddParam {}
@@ -181,11 +174,18 @@ export namespace ReportTask {
protocolCapacity?: string | null protocolCapacity?: string | null
powerSupplyCapacity?: string | null powerSupplyCapacity?: string | null
excelAttachmentName?: string | null excelAttachmentName?: string | null
wordAttachmentName?: string | null
} }
export interface ReportTaskPointGroupItem { export interface ReportTaskPointGroupItem {
groupNo: number groupNo: number
pointIds: string[] pointIds: string[]
reportNo?: string
clientUnitId?: string
createUnit?: string
contractNumber?: string
standard?: string[]
phonenumber?: string
} }
export interface ReportTaskGroupSaveRequest { export interface ReportTaskGroupSaveRequest {
@@ -195,6 +195,12 @@ export namespace ReportTask {
export interface ReportTaskGroupReportRecord { export interface ReportTaskGroupReportRecord {
id?: string id?: string
groupNo?: number | null groupNo?: number | null
reportNo?: string | null
clientUnitId?: string | null
createUnit?: string | null
contractNumber?: string | null
standard?: string[] | string | null
phonenumber?: string | null
reportName?: string | null reportName?: string | null
reportFileName?: string | null reportFileName?: string | null
reportStoragePath?: string | null reportStoragePath?: string | null

View File

@@ -56,12 +56,7 @@ export namespace ReportTaskMulti {
templateDirName: string templateDirName: string
templateDirPath: string templateDirPath: string
no: string no: string
clientUnitId: string
reportModelId: string reportModelId: string
createUnit: string
contractNumber: string
standard: string[]
phonenumber?: string
} }
export interface ReportTaskMultiCreateParam { export interface ReportTaskMultiCreateParam {

View File

@@ -1,19 +1,12 @@
<template> <template>
<div class="form-panel"> <div class="form-panel">
<el-form ref="formRef" :model="formModel" :rules="formRules" label-width="108px" class="report-task-form"> <el-form ref="formRef" :model="localFormModel" :rules="formRules" label-width="108px" class="report-task-form">
<el-row :gutter="16"> <el-row :gutter="16">
<el-col :xs="24" :sm="12"> <el-col :xs="24" :sm="12">
<el-form-item label="报告编号" prop="no"> <el-form-item label="报告编号" prop="no">
<el-input v-model="localFormModel.no" maxlength="32" clearable placeholder="请输入报告编号" /> <el-input v-model="localFormModel.no" maxlength="32" clearable placeholder="请输入报告编号" />
</el-form-item> </el-form-item>
</el-col> </el-col>
<el-col :xs="24" :sm="12">
<el-form-item label="委托单位" prop="clientUnitId">
<el-select v-model="localFormModel.clientUnitId" filterable clearable placeholder="请选择委托单位">
<el-option v-for="option in clientUnitSelectOptions" :key="option.value" :label="option.label" :value="option.value" />
</el-select>
</el-form-item>
</el-col>
<el-col :xs="24" :sm="12"> <el-col :xs="24" :sm="12">
<el-form-item label="报告模板" prop="reportModelId"> <el-form-item label="报告模板" prop="reportModelId">
<el-select v-model="localFormModel.reportModelId" filterable clearable placeholder="请选择报告模板"> <el-select v-model="localFormModel.reportModelId" filterable clearable placeholder="请选择报告模板">
@@ -21,61 +14,15 @@
</el-select> </el-select>
</el-form-item> </el-form-item>
</el-col> </el-col>
<el-col :xs="24" :sm="12">
<el-form-item label="检测公司" prop="createUnit">
<el-select v-model="localFormModel.createUnit" filterable clearable placeholder="请选择检测公司">
<el-option v-for="option in companySelectOptions" :key="option.value" :label="option.label" :value="option.value" />
</el-select>
</el-form-item>
</el-col>
<el-col :xs="24" :sm="12">
<el-form-item label="合同编号" prop="contractNumber">
<el-input v-model="localFormModel.contractNumber" maxlength="32" clearable placeholder="请输入合同编号" />
</el-form-item>
</el-col>
<el-col :xs="24" :sm="12">
<el-form-item label="测试依据" prop="standardIds">
<el-select v-model="localFormModel.standardIds" multiple filterable clearable collapse-tags placeholder="请选择测试依据">
<el-option v-for="option in standardSelectOptions" :key="option.value" :label="option.label" :value="option.value" />
</el-select>
</el-form-item>
</el-col>
<el-col :xs="24" :sm="12">
<el-form-item label="联系电话" prop="phonenumber">
<el-input v-model="localFormModel.phonenumber" maxlength="32" clearable placeholder="请输入联系电话" />
</el-form-item>
</el-col>
</el-row> </el-row>
</el-form> </el-form>
<div class="summary-strip">
<div class="summary-item">
<span class="summary-label">导入状态</span>
<el-tag :type="canSubmit ? 'success' : 'warning'">
{{ canSubmit ? '已准备提交' : '待完成正式导入' }}
</el-tag>
</div>
<div class="summary-item">
<span class="summary-label">监测点数</span>
<span class="summary-value">{{ importSummary.pointCount }}</span>
</div>
<div class="summary-item">
<span class="summary-label">分组数</span>
<span class="summary-value">{{ importSummary.groupCount }}</span>
</div>
<div class="summary-item">
<span class="summary-label">汇总文件</span>
<span class="summary-value">{{ importSummary.summaryFileName }}</span>
</div>
</div>
</div> </div>
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { computed, ref, watch } from 'vue'
import type { FormInstance, FormRules } from 'element-plus' import type { FormInstance, FormRules } from 'element-plus'
import { computed, reactive, ref, watch } from 'vue'
import type { ReportTask } from '@/api/aireport/testReport/interface' import type { ReportTask } from '@/api/aireport/testReport/interface'
import { checkReportTaskLedgerPrepared } from '../utils/testReport'
import type { ReportTaskFormModel } from '../utils/testReportForm' import type { ReportTaskFormModel } from '../utils/testReportForm'
defineOptions({ defineOptions({
@@ -85,28 +32,19 @@ defineOptions({
const props = defineProps<{ const props = defineProps<{
formModel: ReportTaskFormModel formModel: ReportTaskFormModel
record: ReportTask.ReportTaskRecord | null record: ReportTask.ReportTaskRecord | null
clientUnitOptions: Array<{ label: string; value: string }>
reportModelOptions: Array<{ label: string; value: string }> reportModelOptions: Array<{ label: string; value: string }>
companyOptions: Array<{ label: string; value: string }>
standardOptions: Array<{ label: string; value: string }>
preparedState: ReportTask.ReportTaskLedgerPreparedState
}>() }>()
const emit = defineEmits<{ const emit = defineEmits<{
(event: 'register-form', value: FormInstance | undefined): void
(event: 'update:form-model', value: ReportTaskFormModel): void (event: 'update:form-model', value: ReportTaskFormModel): void
}>() }>()
const formRef = ref<FormInstance>() const formRef = ref<FormInstance>()
const localFormModel = reactive<ReportTaskFormModel>({ ...props.formModel }) const localFormModel = ref<ReportTaskFormModel>({ ...props.formModel })
const formRules: FormRules = { const formRules: FormRules = {
no: [{ required: true, message: '请输入报告编号', trigger: 'blur' }], no: [{ required: true, message: '请输入报告编号', trigger: 'blur' }],
clientUnitId: [{ required: true, message: '请选择委托单位', trigger: 'change' }], reportModelId: [{ required: true, message: '请选择报告模板', trigger: 'change' }]
reportModelId: [{ required: true, message: '请选择报告模板', trigger: 'change' }],
createUnit: [{ required: true, message: '请选择检测公司', trigger: 'change' }],
contractNumber: [{ required: true, message: '请输入合同编号', trigger: 'blur' }],
standardIds: [{ required: true, message: '请选择测试依据', trigger: 'change' }]
} }
const ensureCurrentOption = ( const ensureCurrentOption = (
@@ -118,56 +56,29 @@ const ensureCurrentOption = (
return [...options, { label: fallbackLabel || value, value }] return [...options, { label: fallbackLabel || value, value }]
} }
const clientUnitSelectOptions = computed(() =>
ensureCurrentOption(props.clientUnitOptions, localFormModel.clientUnitId, props.record?.clientUnitName || '')
)
const reportModelSelectOptions = computed(() => const reportModelSelectOptions = computed(() =>
ensureCurrentOption(props.reportModelOptions, localFormModel.reportModelId, props.record?.reportModelName || '') ensureCurrentOption(props.reportModelOptions, localFormModel.value.reportModelId, props.record?.reportModelName || '')
)
const companySelectOptions = computed(() =>
ensureCurrentOption(props.companyOptions, localFormModel.createUnit, props.record?.createUnit || '')
)
const standardSelectOptions = computed(() => {
const optionMap = new Map(props.standardOptions.map(option => [option.value, option]))
const extraOptions = localFormModel.standardIds.filter(item => !optionMap.has(item)).map(item => ({ label: item, value: item }))
return [...props.standardOptions, ...extraOptions]
})
const canSubmit = computed(() => checkReportTaskLedgerPrepared(props.preparedState))
const importSummary = computed(() => {
const importResult = props.preparedState.latestImportResult
const snapshot = props.preparedState.snapshot
return {
pointCount: String(importResult?.pointCount ?? snapshot?.rowCount ?? '-'),
groupCount: String(importResult?.groupCount ?? snapshot?.groupSummary?.length ?? '-'),
summaryFileName: importResult?.summaryFileName || snapshot?.summaryFileName || '-'
}
})
watch(
formRef,
value => {
emit('register-form', value)
},
{ immediate: true }
) )
watch( watch(
() => props.formModel, () => props.formModel,
value => { value => {
Object.assign(localFormModel, value) localFormModel.value = { ...value }
},
{ immediate: true, deep: true }
)
watch(
() => localFormModel.value,
value => {
emit('update:form-model', { ...value })
}, },
{ deep: true } { deep: true }
) )
watch( defineExpose({
localFormModel, validate: () => formRef.value?.validate()
value => { })
emit('update:form-model', { ...value, standardIds: [...value.standardIds] })
},
{ deep: true }
)
</script> </script>
<style scoped lang="scss"> <style scoped lang="scss">
@@ -178,44 +89,8 @@ watch(
background: var(--el-fill-color-blank); background: var(--el-fill-color-blank);
} }
.summary-strip {
display: grid;
grid-template-columns: repeat(4, minmax(0, 1fr));
gap: 12px;
margin-top: 8px;
}
.summary-item {
padding: 12px 14px;
border-radius: 10px;
background: var(--el-fill-color-light);
}
.summary-label {
display: block;
margin-bottom: 6px;
font-size: 12px;
color: var(--el-text-color-secondary);
}
.summary-value {
font-size: 14px;
font-weight: 600;
color: var(--el-text-color-primary);
}
:deep(.report-task-form .el-form-item) {
margin-bottom: 18px;
}
:deep(.el-select), :deep(.el-select),
:deep(.el-input) { :deep(.el-input) {
width: 100%; width: 100%;
} }
@media (max-width: 900px) {
.summary-strip {
grid-template-columns: 1fr;
}
}
</style> </style>

View File

@@ -3,16 +3,15 @@
<el-result <el-result
icon="success" icon="success"
:title="mode === 'create' ? '报告任务新增完成' : '报告任务编辑完成'" :title="mode === 'create' ? '报告任务新增完成' : '报告任务编辑完成'"
sub-title="文件导入与基础信息保存均已完成本次结果已同步刷新到列表" sub-title="文件导入与任务基础信息保存均已完成"
/> />
<el-descriptions :column="2" border> <el-descriptions :column="2" border>
<el-descriptions-item label="报告编号">{{ formModel.no || '-' }}</el-descriptions-item> <el-descriptions-item label="报告编号">{{ formModel.no || '-' }}</el-descriptions-item>
<el-descriptions-item label="委托单位">{{ resolveCurrentOptionLabel(clientUnitOptions, formModel.clientUnitId) }}</el-descriptions-item> <el-descriptions-item label="报告模板">{{ resolveCurrentOptionLabel(reportModelOptions, formModel.reportModelId) }}</el-descriptions-item>
<el-descriptions-item label="检测公司">{{ resolveCurrentOptionLabel(companyOptions, formModel.createUnit) }}</el-descriptions-item>
<el-descriptions-item label="监测点数">{{ importSummary.pointCount }}</el-descriptions-item> <el-descriptions-item label="监测点数">{{ importSummary.pointCount }}</el-descriptions-item>
<el-descriptions-item label="分组数">{{ importSummary.groupCount }}</el-descriptions-item> <el-descriptions-item label="分组数">{{ importSummary.groupCount }}</el-descriptions-item>
<el-descriptions-item label="汇总文件">{{ importSummary.summaryFileName }}</el-descriptions-item> <el-descriptions-item label="汇总文件" :span="2">{{ importSummary.summaryFileName }}</el-descriptions-item>
</el-descriptions> </el-descriptions>
</div> </div>
</template> </template>
@@ -29,8 +28,7 @@ const props = defineProps<{
mode: 'create' | 'edit' mode: 'create' | 'edit'
formModel: ReportTaskFormModel formModel: ReportTaskFormModel
preparedState: ReportTask.ReportTaskLedgerPreparedState preparedState: ReportTask.ReportTaskLedgerPreparedState
clientUnitOptions: Array<{ label: string; value: string }> reportModelOptions: Array<{ label: string; value: string }>
companyOptions: Array<{ label: string; value: string }>
}>() }>()
const resolveCurrentOptionLabel = (options: Array<{ label: string; value: string }>, value: string) => const resolveCurrentOptionLabel = (options: Array<{ label: string; value: string }>, value: string) =>

View File

@@ -1,21 +1,13 @@
<template> <template>
<el-dialog v-model="visibleProxy" title="报告任务详情" width="880px" append-to-body destroy-on-close> <el-dialog v-model="visibleProxy" title="报告任务详情" width="1200px" append-to-body destroy-on-close>
<div v-loading="loading" class="report-task-detail-dialog"> <div v-loading="loading" class="report-task-detail-dialog">
<el-descriptions class="detail-descriptions" :column="2" border> <el-descriptions class="detail-descriptions" :column="2" border>
<el-descriptions-item label="报告编号">{{ resolveReportTaskText(detail?.no) }}</el-descriptions-item> <el-descriptions-item label="报告编号">{{ resolveReportTaskText(detail?.no) }}</el-descriptions-item>
<el-descriptions-item label="委托单位">{{ resolveReportTaskText(detail?.clientUnitName) }}</el-descriptions-item>
<el-descriptions-item label="报告模板">{{ resolveReportTaskText(detail?.reportModelName) }}</el-descriptions-item> <el-descriptions-item label="报告模板">{{ resolveReportTaskText(detail?.reportModelName) }}</el-descriptions-item>
<el-descriptions-item label="检测公司"> <!-- <el-descriptions-item label="模板目录">{{ resolveReportTaskText(detail?.templateDirName) }}</el-descriptions-item>-->
{{ resolveReportTaskCreateUnitText(detail?.createUnit, companyLabelMap) }} <!-- <el-descriptions-item label="导入汇总快照数据" :span="2">{{ resolveReportTaskText(detail?.data) }}</el-descriptions-item>-->
</el-descriptions-item>
<el-descriptions-item label="合同编号">{{ resolveReportTaskText(detail?.contractNumber) }}</el-descriptions-item>
<el-descriptions-item label="联系电话">{{ resolveReportTaskText(detail?.phonenumber) }}</el-descriptions-item>
<el-descriptions-item label="测试依据" :span="2">
{{ resolveReportTaskStandardText(detail?.standard, standardLabelMap) }}
</el-descriptions-item>
<el-descriptions-item label="送资数据" :span="2">{{ resolveReportTaskText(detail?.data) }}</el-descriptions-item>
<el-descriptions-item label="监测点数量">{{ resolveReportTaskText(detail?.pointCount) }}</el-descriptions-item> <el-descriptions-item label="监测点数量">{{ resolveReportTaskText(detail?.pointCount) }}</el-descriptions-item>
<el-descriptions-item label="分组数量">{{ resolveReportTaskText(detail?.groupCount) }}</el-descriptions-item> <el-descriptions-item label="分组数量">{{ resolveReportTaskText(displayGroupCount) }}</el-descriptions-item>
<el-descriptions-item label="审核人">{{ resolveReportTaskText(detail?.checkerName) }}</el-descriptions-item> <el-descriptions-item label="审核人">{{ resolveReportTaskText(detail?.checkerName) }}</el-descriptions-item>
<el-descriptions-item label="审核时间">{{ resolveReportTaskText(detail?.checkTime) }}</el-descriptions-item> <el-descriptions-item label="审核时间">{{ resolveReportTaskText(detail?.checkTime) }}</el-descriptions-item>
<el-descriptions-item label="审核意见" :span="2">{{ resolveReportTaskText(detail?.checkResult) }}</el-descriptions-item> <el-descriptions-item label="审核意见" :span="2">{{ resolveReportTaskText(detail?.checkResult) }}</el-descriptions-item>
@@ -30,10 +22,28 @@
</el-descriptions> </el-descriptions>
<div class="group-report-section"> <div class="group-report-section">
<div class="group-report-title">分组报告生成状态</div> <div class="group-report-title">分组报告</div>
<el-empty v-if="!groupReports.length" description="暂无分组报告记录" :image-size="88" /> <el-empty v-if="!groupReports.length" description="暂无分组报告记录" :image-size="88" />
<el-table v-else :data="groupReports" border size="small"> <el-table v-else :data="groupReports" border size="small">
<el-table-column prop="groupNo" label="分组号" width="100" /> <el-table-column prop="groupNo" label="分组号" width="90" />
<el-table-column prop="reportNo" label="报告编号" min-width="160" show-overflow-tooltip>
<template #default="{ row }">{{ resolveReportTaskText(row.reportNo) }}</template>
</el-table-column>
<el-table-column label="委托单位" min-width="180" show-overflow-tooltip>
<template #default="{ row }">{{ resolveReportTaskClientUnitText(row.clientUnitId, clientUnitLabelMap) }}</template>
</el-table-column>
<el-table-column label="检测公司" min-width="160" show-overflow-tooltip>
<template #default="{ row }">{{ resolveReportTaskCreateUnitText(row.createUnit, companyLabelMap) }}</template>
</el-table-column>
<el-table-column prop="contractNumber" label="合同编号" min-width="160" show-overflow-tooltip>
<template #default="{ row }">{{ resolveReportTaskText(row.contractNumber) }}</template>
</el-table-column>
<el-table-column label="检测依据" min-width="220" show-overflow-tooltip>
<template #default="{ row }">{{ resolveReportTaskStandardText(row.standard, standardLabelMap) }}</template>
</el-table-column>
<el-table-column prop="phonenumber" label="联系电话" min-width="140" show-overflow-tooltip>
<template #default="{ row }">{{ resolveReportTaskText(row.phonenumber) }}</template>
</el-table-column>
<el-table-column prop="reportName" label="报告名称" min-width="180" show-overflow-tooltip /> <el-table-column prop="reportName" label="报告名称" min-width="180" show-overflow-tooltip />
<el-table-column label="生成状态" width="120"> <el-table-column label="生成状态" width="120">
<template #default="{ row }"> <template #default="{ row }">
@@ -70,6 +80,7 @@
import { computed } from 'vue' import { computed } from 'vue'
import type { ReportTask } from '@/api/aireport/testReport/interface' import type { ReportTask } from '@/api/aireport/testReport/interface'
import { import {
normalizeReportTaskLedgerSnapshot,
resolveReportTaskCreateUnitText, resolveReportTaskCreateUnitText,
resolveReportTaskGroupReportGenerateStateTagType, resolveReportTaskGroupReportGenerateStateTagType,
resolveReportTaskGroupReportGenerateStateText, resolveReportTaskGroupReportGenerateStateText,
@@ -86,6 +97,7 @@ const props = defineProps<{
loading: boolean loading: boolean
detail: ReportTask.ReportTaskRecord | null detail: ReportTask.ReportTaskRecord | null
groupReports: ReportTask.ReportTaskGroupReportRecord[] groupReports: ReportTask.ReportTaskGroupReportRecord[]
clientUnitLabelMap: Record<string, string>
companyLabelMap: Record<string, string> companyLabelMap: Record<string, string>
standardLabelMap: Record<string, string> standardLabelMap: Record<string, string>
downloadingGroupReportId?: string | null downloadingGroupReportId?: string | null
@@ -101,10 +113,28 @@ const visibleProxy = computed({
set: value => emit('update:visible', value) set: value => emit('update:visible', value)
}) })
const clientUnitLabelMap = computed(() => props.clientUnitLabelMap)
const companyLabelMap = computed(() => props.companyLabelMap) const companyLabelMap = computed(() => props.companyLabelMap)
const standardLabelMap = computed(() => props.standardLabelMap) const standardLabelMap = computed(() => props.standardLabelMap)
const groupReports = computed(() => props.groupReports || []) const groupReports = computed(() => props.groupReports || [])
const downloadingGroupReportId = computed(() => props.downloadingGroupReportId || '') const downloadingGroupReportId = computed(() => props.downloadingGroupReportId || '')
const detailSnapshot = computed(() => normalizeReportTaskLedgerSnapshot(props.detail?.data as string | null | undefined))
const displayGroupCount = computed(() => {
if (groupReports.value.length > 0) {
return groupReports.value.length
}
const snapshotGroupCount = Number(detailSnapshot.value?.groupSummary?.length ?? 0)
if (Number.isInteger(snapshotGroupCount) && snapshotGroupCount > 0) {
return snapshotGroupCount
}
return props.detail?.groupCount
})
const resolveReportTaskClientUnitText = (value: string | null | undefined, labelMap: Record<string, string>) => {
const text = String(value || '').trim()
if (!text) return '-'
return labelMap[text] || text
}
</script> </script>
<style scoped lang="scss"> <style scoped lang="scss">

View File

@@ -51,29 +51,7 @@
<el-row :gutter="16"> <el-row :gutter="16">
<el-col :xs="24" :sm="12"> <el-col :xs="24" :sm="12">
<el-form-item label="报告编号" prop="no"> <el-form-item label="报告编号" prop="no">
<el-input <el-input v-model="formModel.no" maxlength="32" clearable placeholder="请输入报告编号" />
v-model="formModel.no"
maxlength="32"
clearable
placeholder="请输入报告编号"
/>
</el-form-item>
</el-col>
<el-col :xs="24" :sm="12">
<el-form-item label="委托单位" prop="clientUnitId">
<el-select
v-model="formModel.clientUnitId"
filterable
clearable
placeholder="请选择委托单位"
>
<el-option
v-for="option in clientUnitSelectOptions"
:key="option.value"
:label="option.label"
:value="option.value"
/>
</el-select>
</el-form-item> </el-form-item>
</el-col> </el-col>
<el-col :xs="24" :sm="12"> <el-col :xs="24" :sm="12">
@@ -93,62 +71,6 @@
</el-select> </el-select>
</el-form-item> </el-form-item>
</el-col> </el-col>
<el-col :xs="24" :sm="12">
<el-form-item label="检测公司" prop="createUnit">
<el-select
v-model="formModel.createUnit"
filterable
clearable
placeholder="请选择检测公司"
>
<el-option
v-for="option in companySelectOptions"
:key="option.value"
:label="option.label"
:value="option.value"
/>
</el-select>
</el-form-item>
</el-col>
<el-col :xs="24" :sm="12">
<el-form-item label="合同编号" prop="contractNumber">
<el-input
v-model="formModel.contractNumber"
maxlength="32"
clearable
placeholder="请输入合同编号"
/>
</el-form-item>
</el-col>
<el-col :xs="24" :sm="12">
<el-form-item label="测试依据" prop="standardIds">
<el-select
v-model="formModel.standardIds"
multiple
filterable
clearable
collapse-tags
placeholder="请选择测试依据"
>
<el-option
v-for="option in standardSelectOptions"
:key="option.value"
:label="option.label"
:value="option.value"
/>
</el-select>
</el-form-item>
</el-col>
<el-col :xs="24" :sm="12">
<el-form-item label="联系电话" prop="phonenumber">
<el-input
v-model="formModel.phonenumber"
maxlength="32"
clearable
placeholder="请输入联系电话"
/>
</el-form-item>
</el-col>
</el-row> </el-row>
</el-form> </el-form>
@@ -180,20 +102,17 @@
<el-result <el-result
icon="success" icon="success"
:title="mode === 'create' ? '报告任务新增完成' : '报告任务编辑完成'" :title="mode === 'create' ? '报告任务新增完成' : '报告任务编辑完成'"
sub-title="台账校验与基础信息保存均已完成本次结果已同步刷新到列表" sub-title="台账校验与任务基础信息保存均已完成"
/> />
<el-descriptions :column="2" border> <el-descriptions :column="2" border>
<el-descriptions-item label="报告编号">{{ formModel.no || '-' }}</el-descriptions-item> <el-descriptions-item label="报告编号">{{ formModel.no || '-' }}</el-descriptions-item>
<el-descriptions-item label="委托单位"> <el-descriptions-item label="报告模板">
{{ resolveCurrentOptionLabel(clientUnitSelectOptions, formModel.clientUnitId) }} {{ resolveCurrentOptionLabel(reportModelSelectOptions, formModel.reportModelId) }}
</el-descriptions-item>
<el-descriptions-item label="检测公司">
{{ resolveCurrentOptionLabel(companySelectOptions, formModel.createUnit) }}
</el-descriptions-item> </el-descriptions-item>
<el-descriptions-item label="监测点数">{{ importSummary.pointCount }}</el-descriptions-item> <el-descriptions-item label="监测点数">{{ importSummary.pointCount }}</el-descriptions-item>
<el-descriptions-item label="分组数">{{ importSummary.groupCount }}</el-descriptions-item> <el-descriptions-item label="分组数">{{ importSummary.groupCount }}</el-descriptions-item>
<el-descriptions-item label="汇总文件"> <el-descriptions-item label="汇总文件" :span="2">
{{ importSummary.summaryFileName }} {{ importSummary.summaryFileName }}
</el-descriptions-item> </el-descriptions-item>
</el-descriptions> </el-descriptions>
@@ -203,11 +122,7 @@
<div class="log-panel" :class="{ 'is-collapsed': !logPanelExpanded }"> <div class="log-panel" :class="{ 'is-collapsed': !logPanelExpanded }">
<button type="button" class="log-panel-header" @click="logPanelExpanded = !logPanelExpanded"> <button type="button" class="log-panel-header" @click="logPanelExpanded = !logPanelExpanded">
<span <span class="log-panel-toggle" :class="{ 'is-collapsed': !logPanelExpanded }" aria-hidden="true"></span>
class="log-panel-toggle"
:class="{ 'is-collapsed': !logPanelExpanded }"
aria-hidden="true"
></span>
<div class="log-title">日志信息</div> <div class="log-title">日志信息</div>
</button> </button>
@@ -253,15 +168,7 @@ import { computed, reactive, ref, watch } from 'vue'
import type { ReportTask } from '@/api/aireport/testReport/interface' import type { ReportTask } from '@/api/aireport/testReport/interface'
import { generateUUID } from '@/utils' import { generateUUID } from '@/utils'
import ReportTaskLedgerImportPanel from './ReportTaskLedgerImportPanel.vue' import ReportTaskLedgerImportPanel from './ReportTaskLedgerImportPanel.vue'
import { import { checkReportTaskLedgerPrepared, getReportTaskErrorMessage, normalizeReportTaskLedgerSnapshot } from '../utils/testReport'
checkReportTaskLedgerPrepared,
getReportTaskErrorMessage,
normalizeReportTaskLedgerSnapshot,
normalizeReportTaskSelectedOptionValues,
parseReportTaskStandardIds,
resolveReportTaskOptionValue,
stringifyReportTaskStandardIds
} from '../utils/testReport'
defineOptions({ defineOptions({
name: 'ReportTaskFormDialog' name: 'ReportTaskFormDialog'
@@ -276,24 +183,9 @@ const REPORT_TASK_FORM_STEPS: Array<{
title: string title: string
description: string description: string
}> = [ }> = [
{ { index: 1, value: 'import', title: '数据导入', description: '先完成台账与附件校验' },
index: 1, { index: 2, value: 'form', title: '任务信息', description: '填写任务编号和报告模板' },
value: 'import', { index: 3, value: 'done', title: '完成', description: '查看结果并结束本次流程' }
title: '数据导入',
description: '先完成台账与附件校验'
},
{
index: 2,
value: 'form',
title: '基础信息填写',
description: '填写并确认任务基础信息'
},
{
index: 3,
value: 'done',
title: '完成',
description: '查看结果并结束本次流程'
}
] ]
const props = defineProps<{ const props = defineProps<{
@@ -335,22 +227,13 @@ const lastHandledSaveSuccessVersion = ref(0)
const formModel = reactive({ const formModel = reactive({
id: '', id: '',
no: '', no: '',
clientUnitId: '',
reportModelId: '', reportModelId: '',
createUnit: '', data: '{}'
contractNumber: '',
standardIds: [] as string[],
data: '{}',
phonenumber: ''
}) })
const formRules: FormRules = { const formRules: FormRules = {
no: [{ required: true, message: '请输入报告编号', trigger: 'blur' }], no: [{ required: true, message: '请输入报告编号', trigger: 'blur' }],
clientUnitId: [{ required: true, message: '请选择委托单位', trigger: 'change' }], reportModelId: [{ required: true, message: '请选择报告模板', trigger: 'change' }]
reportModelId: [{ required: true, message: '请选择报告模板', trigger: 'change' }],
createUnit: [{ required: true, message: '请选择检测公司', trigger: 'change' }],
contractNumber: [{ required: true, message: '请输入合同编号', trigger: 'blur' }],
standardIds: [{ required: true, message: '请选择测试依据', trigger: 'change' }]
} }
const visibleProxy = computed({ const visibleProxy = computed({
@@ -377,27 +260,12 @@ const ensureCurrentOption = (
fallbackLabel: string fallbackLabel: string
) => { ) => {
if (!value || options.some(option => option.value === value)) return options if (!value || options.some(option => option.value === value)) return options
return [...options, { label: fallbackLabel || value, value }] return [...options, { label: fallbackLabel || value, value }]
} }
const clientUnitSelectOptions = computed(() =>
ensureCurrentOption(props.clientUnitOptions, formModel.clientUnitId, props.record?.clientUnitName || '')
)
const reportModelSelectOptions = computed(() => const reportModelSelectOptions = computed(() =>
ensureCurrentOption(props.reportModelOptions, formModel.reportModelId, props.record?.reportModelName || '') ensureCurrentOption(props.reportModelOptions, formModel.reportModelId, props.record?.reportModelName || '')
) )
const companySelectOptions = computed(() =>
ensureCurrentOption(props.companyOptions, formModel.createUnit, props.record?.createUnit || '')
)
const standardSelectOptions = computed(() => {
const optionMap = new Map(props.standardOptions.map(option => [option.value, option]))
const extraOptions = formModel.standardIds
.filter(item => !optionMap.has(item))
.map(item => ({ label: item, value: item }))
return [...props.standardOptions, ...extraOptions]
})
const buildLogId = () => window.crypto?.randomUUID?.() || generateUUID() const buildLogId = () => window.crypto?.randomUUID?.() || generateUUID()
@@ -435,13 +303,8 @@ const resetForm = () => {
formRef.value?.clearValidate() formRef.value?.clearValidate()
formModel.id = '' formModel.id = ''
formModel.no = '' formModel.no = ''
formModel.clientUnitId = ''
formModel.reportModelId = '' formModel.reportModelId = ''
formModel.createUnit = ''
formModel.contractNumber = ''
formModel.standardIds = []
formModel.data = '{}' formModel.data = '{}'
formModel.phonenumber = ''
activeDraftId.value = '' activeDraftId.value = ''
currentStep.value = 'import' currentStep.value = 'import'
logPanelExpanded.value = true logPanelExpanded.value = true
@@ -464,17 +327,15 @@ const buildDraftId = () => {
if (props.mode === 'edit') { if (props.mode === 'edit') {
return String(props.record?.id || '').trim() return String(props.record?.id || '').trim()
} }
return window.crypto?.randomUUID?.() || generateUUID() return window.crypto?.randomUUID?.() || generateUUID()
} }
const applyInitialStep = (hasPreparedLedger: boolean) => { const applyInitialStep = (hasPreparedLedger: boolean) => {
if (props.mode === 'edit' && hasPreparedLedger) { if (props.mode === 'edit' && hasPreparedLedger) {
currentStep.value = 'form' currentStep.value = 'form'
pushLog('info', '编辑场景检测到已有导入结果,默认进入基础信息填写步骤') pushLog('info', '编辑场景检测到已有导入结果,默认进入任务信息步骤')
return return
} }
currentStep.value = 'import' currentStep.value = 'import'
pushLog('info', '已进入数据导入步骤,请先完成台账校验') pushLog('info', '已进入数据导入步骤,请先完成台账校验')
} }
@@ -482,21 +343,15 @@ const applyInitialStep = (hasPreparedLedger: boolean) => {
const fillForm = async () => { const fillForm = async () => {
try { try {
const record = props.record const record = props.record
const snapshot = normalizeReportTaskLedgerSnapshot(record?.data) const snapshot = normalizeReportTaskLedgerSnapshot(record?.data as string | null | undefined)
formModel.id = record?.id || '' formModel.id = record?.id || ''
formModel.no = record?.no || '' formModel.no = record?.no || ''
formModel.clientUnitId = record?.clientUnitId || ''
formModel.reportModelId = record?.reportModelId || '' formModel.reportModelId = record?.reportModelId || ''
// 关键业务节点:编辑态详情可能返回展示名称而不是字典 value这里必须先归一化成真实 option value避免下拉重复补项和提交时报“检测公司不存在”。 formModel.data =
formModel.createUnit = resolveReportTaskOptionValue(props.companyOptions, record?.createUnit || '') typeof record?.data === 'string' && record.data.trim()
formModel.contractNumber = record?.contractNumber || '' ? record.data
formModel.standardIds = normalizeReportTaskSelectedOptionValues( : JSON.stringify(record?.data || snapshot || {}, null, 0) || '{}'
props.standardOptions,
parseReportTaskStandardIds(record?.standard)
)
formModel.data = record?.data || '{}'
formModel.phonenumber = record?.phonenumber || ''
activeDraftId.value = buildDraftId() activeDraftId.value = buildDraftId()
ledgerPreparedState.value = { ledgerPreparedState.value = {
@@ -525,12 +380,11 @@ const fillForm = async () => {
const goToFormStep = () => { const goToFormStep = () => {
if (!canSubmit.value) { if (!canSubmit.value) {
pushLog('warning', '当前校验尚未完成,不能进入基础信息填写步骤') pushLog('warning', '当前校验尚未完成,不能进入任务信息步骤')
return return
} }
currentStep.value = 'form' currentStep.value = 'form'
pushLog('info', '已切换到基础信息填写步骤') pushLog('info', '已切换到任务信息步骤')
} }
const goToImportStep = () => { const goToImportStep = () => {
@@ -540,16 +394,11 @@ const goToImportStep = () => {
const handleLedgerPreparedChange = (value: ReportTask.ReportTaskLedgerPreparedState) => { const handleLedgerPreparedChange = (value: ReportTask.ReportTaskLedgerPreparedState) => {
const wasPrepared = checkReportTaskLedgerPrepared(ledgerPreparedState.value) const wasPrepared = checkReportTaskLedgerPrepared(ledgerPreparedState.value)
const nextState = { const nextState = { ...value, draftId: activeDraftId.value }
...value,
draftId: activeDraftId.value
}
ledgerPreparedState.value = nextState ledgerPreparedState.value = nextState
if (!wasPrepared && checkReportTaskLedgerPrepared(nextState) && currentStep.value === 'import') { if (!wasPrepared && checkReportTaskLedgerPrepared(nextState) && currentStep.value === 'import') {
currentStep.value = 'form' currentStep.value = 'form'
pushLog('success', '台账校验已完成,已自动进入基础信息填写步骤') pushLog('success', '台账校验已完成,已自动进入任务信息步骤')
} }
} }
@@ -591,7 +440,6 @@ watch(
() => props.saveSuccessVersion, () => props.saveSuccessVersion,
value => { value => {
if (!props.visible || !value || value === lastHandledSaveSuccessVersion.value) return if (!props.visible || !value || value === lastHandledSaveSuccessVersion.value) return
lastHandledSaveSuccessVersion.value = value lastHandledSaveSuccessVersion.value = value
currentStep.value = 'done' currentStep.value = 'done'
pushLog('success', props.mode === 'create' ? '报告任务新增成功,流程已完成' : '报告任务编辑成功,流程已完成') pushLog('success', props.mode === 'create' ? '报告任务新增成功,流程已完成' : '报告任务编辑成功,流程已完成')
@@ -601,34 +449,24 @@ watch(
const submitForm = async () => { const submitForm = async () => {
const valid = await formRef.value?.validate().catch(() => false) const valid = await formRef.value?.validate().catch(() => false)
if (!valid) { if (!valid) {
pushLog('warning', '基础信息校验未通过,请检查必填项') pushLog('warning', '任务信息校验未通过,请检查必填项')
return return
} }
if (!checkReportTaskLedgerPrepared(ledgerPreparedState.value)) { if (!checkReportTaskLedgerPrepared(ledgerPreparedState.value)) {
const message = '请先完成台账校验后再提交基础信息' const message = '请先完成台账校验后再提交任务信息'
ElMessage.warning(message) ElMessage.warning(message)
pushLog('warning', message) pushLog('warning', message)
return return
} }
pushLog( pushLog('info', props.mode === 'create' ? '开始提交新增任务' : '开始提交编辑后的任务信息')
'info',
props.mode === 'create'
? '开始提交新增任务基础信息并写入台账数据'
: '开始提交编辑后的任务基础信息并更新台账数据'
)
emit('submit', { emit('submit', {
id: activeDraftId.value || formModel.id || undefined, id: activeDraftId.value || formModel.id || undefined,
no: formModel.no.trim(), no: formModel.no.trim(),
clientUnitId: formModel.clientUnitId,
reportModelId: formModel.reportModelId, reportModelId: formModel.reportModelId,
createUnit: formModel.createUnit,
contractNumber: formModel.contractNumber.trim(),
standard: stringifyReportTaskStandardIds(formModel.standardIds),
data: JSON.parse(formModel.data.trim() || '{}'), data: JSON.parse(formModel.data.trim() || '{}'),
batchId: ledgerPreparedState.value.batchId || undefined, batchId: ledgerPreparedState.value.batchId || undefined
phonenumber: formModel.phonenumber.trim() || undefined
}) })
} }
</script> </script>
@@ -907,10 +745,6 @@ const submitForm = async () => {
text-overflow: ellipsis; text-overflow: ellipsis;
} }
.log-empty {
padding: 18px 0;
}
:deep(.report-task-flow-dialog .el-dialog__body) { :deep(.report-task-flow-dialog .el-dialog__body) {
overflow: hidden; overflow: hidden;
} }

View File

@@ -1,9 +1,8 @@
<template> <template>
<el-dialog v-model="visibleProxy" title="监测点分组" width="1120px" append-to-body destroy-on-close> <el-dialog v-model="visibleProxy" title="监测点分组" width="1380px" append-to-body destroy-on-close>
<div v-loading="loading" class="report-task-group-dialog"> <div v-loading="loading" class="report-task-group-dialog">
<el-descriptions :column="4" border class="summary-descriptions"> <el-descriptions :column="4" border class="summary-descriptions">
<el-descriptions-item label="报告编号">{{ resolveReportTaskText(record?.no) }}</el-descriptions-item> <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="监测点数">{{ pointList.length || 0 }}</el-descriptions-item>
<el-descriptions-item label="已分组点位">{{ groupedPointCount }}</el-descriptions-item> <el-descriptions-item label="已分组点位">{{ groupedPointCount }}</el-descriptions-item>
<el-descriptions-item label="分组数量">{{ groupedPayload.groups.length || 0 }}</el-descriptions-item> <el-descriptions-item label="分组数量">{{ groupedPayload.groups.length || 0 }}</el-descriptions-item>
@@ -14,7 +13,7 @@
<template v-else> <template v-else>
<el-alert <el-alert
v-if="!groupingCompleted" v-if="!groupingCompleted"
title="请为所有监测点设置大于 0 的分组号后再保存。" title="台账导入时会读取 Excel 中的分组号,也可在此按需调整。当前存在未分组监测点,请补充分组号后再保存。"
type="warning" type="warning"
:closable="false" :closable="false"
show-icon show-icon
@@ -22,20 +21,8 @@
/> />
<div class="toolbar"> <div class="toolbar">
<div class="toolbar-title">批量分组</div> <div class="toolbar-title">监测点分组</div>
<div class="toolbar-actions"> <div class="toolbar-tip">分组号默认来自已导入的台账 Excel可在此按需修改</div>
<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> </div>
<el-table <el-table
@@ -43,12 +30,9 @@
row-key="id" row-key="id"
border border
size="small" size="small"
max-height="460" max-height="420"
class="point-table" 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"> <el-table-column label="分组号" width="120">
<template #default="{ row }"> <template #default="{ row }">
<el-input-number <el-input-number
@@ -64,7 +48,118 @@
<el-table-column prop="pointName" 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="monitorTime" label="监测时间" min-width="160" show-overflow-tooltip />
<el-table-column prop="voltageLevel" label="电压等级" min-width="120" 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-column prop="excelAttachmentName" label="Excel原始文件" min-width="220" show-overflow-tooltip />
<el-table-column prop="wordAttachmentName" label="Word原始文件" min-width="220" show-overflow-tooltip>
<template #default="{ row }">
{{ resolveReportTaskText(row.wordAttachmentName) }}
</template>
</el-table-column>
</el-table>
<div class="toolbar secondary-toolbar">
<div class="toolbar-title">分组报告信息</div>
<div class="toolbar-tip">这些字段允许先保存为空执行生成报告时再由后端做完整校验</div>
</div>
<el-empty
v-if="!groupedRows.length"
description="请先完成监测点分组,再维护各分组的报告信息"
:image-size="72"
class="group-empty"
/>
<el-table v-else :data="groupedRows" border size="small" max-height="420" class="group-report-table">
<el-table-column prop="groupNo" label="分组号" width="90" />
<el-table-column prop="pointCount" label="点位数" width="90" />
<el-table-column label="报告编号" min-width="180">
<template #default="{ row }">
<el-input
:model-value="resolveGroupField(row.groupNo, 'reportNo')"
maxlength="32"
clearable
placeholder="请输入报告编号"
@update:model-value="updateGroupField(row.groupNo, 'reportNo', $event)"
/>
</template>
</el-table-column>
<el-table-column label="委托单位" min-width="180">
<template #default="{ row }">
<el-select
:model-value="resolveGroupField(row.groupNo, 'clientUnitId')"
filterable
clearable
placeholder="请选择委托单位"
@update:model-value="updateGroupField(row.groupNo, 'clientUnitId', $event)"
>
<el-option
v-for="option in clientUnitOptions"
:key="option.value"
:label="option.label"
:value="option.value"
/>
</el-select>
</template>
</el-table-column>
<el-table-column label="检测公司" min-width="180">
<template #default="{ row }">
<el-select
:model-value="resolveGroupField(row.groupNo, 'createUnit')"
filterable
clearable
placeholder="请选择检测公司"
@update:model-value="updateGroupField(row.groupNo, 'createUnit', $event)"
>
<el-option
v-for="option in companyOptions"
:key="option.value"
:label="option.label"
:value="option.value"
/>
</el-select>
</template>
</el-table-column>
<el-table-column label="合同编号" min-width="160">
<template #default="{ row }">
<el-input
:model-value="resolveGroupField(row.groupNo, 'contractNumber')"
maxlength="32"
clearable
placeholder="请输入合同编号"
@update:model-value="updateGroupField(row.groupNo, 'contractNumber', $event)"
/>
</template>
</el-table-column>
<el-table-column label="检测依据" min-width="220">
<template #default="{ row }">
<el-select
:model-value="resolveGroupStandard(row.groupNo)"
multiple
filterable
clearable
collapse-tags
placeholder="请选择检测依据"
@update:model-value="updateGroupStandard(row.groupNo, $event)"
>
<el-option
v-for="option in standardOptions"
:key="option.value"
:label="option.label"
:value="option.value"
/>
</el-select>
</template>
</el-table-column>
<el-table-column label="联系电话" min-width="160">
<template #default="{ row }">
<el-input
:model-value="resolveGroupField(row.groupNo, 'phonenumber')"
maxlength="32"
clearable
placeholder="请输入联系电话"
@update:model-value="updateGroupField(row.groupNo, 'phonenumber', $event)"
/>
</template>
</el-table-column>
</el-table> </el-table>
</template> </template>
</div> </div>
@@ -80,22 +175,31 @@
<script setup lang="ts"> <script setup lang="ts">
import { computed, ref, watch } from 'vue' import { computed, ref, watch } from 'vue'
import type { ReportTask } from '@/api/aireport/testReport/interface' import type { ReportTask } from '@/api/aireport/testReport/interface'
import { import { normalizeReportTaskGroupSavePayload, parseReportTaskStandardIds, resolveReportTaskText } from '../utils/testReport'
checkReportTaskGroupingCompleted,
normalizeReportTaskGroupSavePayload,
resolveReportTaskText
} from '../utils/testReport'
defineOptions({ defineOptions({
name: 'ReportTaskGroupDialog' name: 'ReportTaskGroupDialog'
}) })
type GroupConfig = {
reportNo: string
clientUnitId: string
createUnit: string
contractNumber: string
standard: string[]
phonenumber: string
}
const props = defineProps<{ const props = defineProps<{
visible: boolean visible: boolean
loading: boolean loading: boolean
saving: boolean saving: boolean
record: ReportTask.ReportTaskRecord | null record: ReportTask.ReportTaskRecord | null
pointList: ReportTask.ReportTaskPointRecord[] pointList: ReportTask.ReportTaskPointRecord[]
groupReports: ReportTask.ReportTaskGroupReportRecord[]
clientUnitOptions: Array<{ label: string; value: string }>
companyOptions: Array<{ label: string; value: string }>
standardOptions: Array<{ label: string; value: string }>
}>() }>()
const emit = defineEmits<{ const emit = defineEmits<{
@@ -104,51 +208,109 @@ const emit = defineEmits<{
}>() }>()
const localPointGroupMap = ref<Record<string, number | null>>({}) const localPointGroupMap = ref<Record<string, number | null>>({})
const selectedPointIds = ref<string[]>([]) const localGroupConfigMap = ref<Record<number, GroupConfig>>({})
const batchGroupNo = ref<number | null>(null)
const visibleProxy = computed({ const visibleProxy = computed({
get: () => props.visible, get: () => props.visible,
set: value => emit('update:visible', value) set: value => emit('update:visible', value)
}) })
const createEmptyGroupConfig = (): GroupConfig => ({
reportNo: '',
clientUnitId: '',
createUnit: '',
contractNumber: '',
standard: [],
phonenumber: ''
})
const initializePointGroupMap = () => { const initializePointGroupMap = () => {
const nextGroupMap: Record<string, number | null> = {} const nextGroupMap: Record<string, number | null> = {}
props.pointList.forEach(point => { props.pointList.forEach(point => {
const pointId = String(point.id || '').trim() const pointId = String(point.id || '').trim()
if (!pointId) return if (!pointId) return
const groupNo = Number(point.groupNo) const groupNo = Number(point.groupNo)
nextGroupMap[pointId] = Number.isInteger(groupNo) && groupNo > 0 ? groupNo : null nextGroupMap[pointId] = Number.isInteger(groupNo) && groupNo > 0 ? groupNo : null
}) })
localPointGroupMap.value = nextGroupMap localPointGroupMap.value = nextGroupMap
selectedPointIds.value = [] }
batchGroupNo.value = null
const initializeGroupConfigMap = () => {
const nextConfigMap: Record<number, GroupConfig> = {}
;(props.groupReports || []).forEach(groupReport => {
const groupNo = Number(groupReport.groupNo)
if (!Number.isInteger(groupNo) || groupNo <= 0) return
nextConfigMap[groupNo] = {
reportNo: String(groupReport.reportNo || ''),
clientUnitId: String(groupReport.clientUnitId || ''),
createUnit: String(groupReport.createUnit || ''),
contractNumber: String(groupReport.contractNumber || ''),
standard: parseReportTaskStandardIds(groupReport.standard),
phonenumber: String(groupReport.phonenumber || '')
}
})
localGroupConfigMap.value = nextConfigMap
}
const syncGroupedConfigMap = () => {
const activeGroupNos = new Set<number>()
Object.values(localPointGroupMap.value).forEach(groupNo => {
const normalizedGroupNo = Number(groupNo)
if (Number.isInteger(normalizedGroupNo) && normalizedGroupNo > 0) {
activeGroupNos.add(normalizedGroupNo)
}
})
const nextConfigMap: Record<number, GroupConfig> = {}
Array.from(activeGroupNos)
.sort((a, b) => a - b)
.forEach(groupNo => {
nextConfigMap[groupNo] = localGroupConfigMap.value[groupNo] || createEmptyGroupConfig()
})
localGroupConfigMap.value = nextConfigMap
} }
watch( watch(
() => [props.visible, props.pointList], () => [props.visible, props.pointList, props.groupReports],
([visible]) => { ([visible]) => {
if (visible) { if (!visible) return
initializePointGroupMap() initializePointGroupMap()
} initializeGroupConfigMap()
syncGroupedConfigMap()
}, },
{ immediate: true, deep: true } { immediate: true, deep: true }
) )
const groupedPayload = computed(() => normalizeReportTaskGroupSavePayload(props.pointList, localPointGroupMap.value)) watch(
const groupingCompleted = computed(() => checkReportTaskGroupingCompleted(props.pointList, groupedPayload.value)) () => localPointGroupMap.value,
() => {
syncGroupedConfigMap()
},
{ deep: true }
)
const groupedPayload = computed(() => normalizeReportTaskGroupSavePayload(props.pointList, localPointGroupMap.value, localGroupConfigMap.value))
const groupingCompleted = computed(() => {
if (!props.pointList.length || !groupedPayload.value.groups.length) return false
const pointIds = props.pointList.map(point => String(point.id || '').trim()).filter(Boolean)
const groupedIds = groupedPayload.value.groups.flatMap(group => group.pointIds.map(pointId => String(pointId || '').trim()).filter(Boolean))
if (groupedIds.length !== pointIds.length) return false
return new Set(groupedIds).size === pointIds.length
})
const groupedPointCount = computed(() => const groupedPointCount = computed(() =>
props.pointList.reduce((count, point) => { props.pointList.reduce((count, point) => {
const pointId = String(point.id || '').trim() const pointId = String(point.id || '').trim()
if (!pointId) return count if (!pointId) return count
const groupNo = Number(localPointGroupMap.value[pointId]) const groupNo = Number(localPointGroupMap.value[pointId])
return Number.isInteger(groupNo) && groupNo > 0 ? count + 1 : count return Number.isInteger(groupNo) && groupNo > 0 ? count + 1 : count
}, 0) }, 0)
) )
const groupedRows = computed(() =>
groupedPayload.value.groups.map(group => ({
groupNo: group.groupNo,
pointCount: group.pointIds.length
}))
)
const resolvePointGroupNo = (pointId?: string) => { const resolvePointGroupNo = (pointId?: string) => {
const normalizedPointId = String(pointId || '').trim() const normalizedPointId = String(pointId || '').trim()
@@ -159,7 +321,6 @@ const resolvePointGroupNo = (pointId?: string) => {
const updatePointGroupNo = (pointId: string | undefined, value: number | null | undefined) => { const updatePointGroupNo = (pointId: string | undefined, value: number | null | undefined) => {
const normalizedPointId = String(pointId || '').trim() const normalizedPointId = String(pointId || '').trim()
if (!normalizedPointId) return if (!normalizedPointId) return
const groupNo = Number(value) const groupNo = Number(value)
localPointGroupMap.value = { localPointGroupMap.value = {
...localPointGroupMap.value, ...localPointGroupMap.value,
@@ -171,27 +332,42 @@ const handlePointGroupNoChange = (pointId: string | undefined, value: number | n
updatePointGroupNo(pointId, value) updatePointGroupNo(pointId, value)
} }
const handleSelectionChange = (selection: ReportTask.ReportTaskPointRecord[]) => { const ensureGroupConfig = (groupNo: number) => {
selectedPointIds.value = selection.map(item => String(item.id || '').trim()).filter(Boolean) if (!localGroupConfigMap.value[groupNo]) {
localGroupConfigMap.value = {
...localGroupConfigMap.value,
[groupNo]: createEmptyGroupConfig()
}
}
return localGroupConfigMap.value[groupNo]
} }
const applyBatchGroupNo = () => { const resolveGroupField = (groupNo: number, field: keyof Omit<GroupConfig, 'standard'>) =>
const groupNo = Number(batchGroupNo.value) ensureGroupConfig(groupNo)[field] || ''
if (!Number.isInteger(groupNo) || groupNo <= 0) return
const nextGroupMap = { ...localPointGroupMap.value } const resolveGroupStandard = (groupNo: number) => ensureGroupConfig(groupNo).standard || []
selectedPointIds.value.forEach(pointId => {
nextGroupMap[pointId] = groupNo const updateGroupField = (groupNo: number, field: keyof Omit<GroupConfig, 'standard'>, value: unknown) => {
}) const groupConfig = ensureGroupConfig(groupNo)
localPointGroupMap.value = nextGroupMap localGroupConfigMap.value = {
...localGroupConfigMap.value,
[groupNo]: {
...groupConfig,
[field]: String(value || '')
}
}
} }
const clearSelectedGroups = () => { const updateGroupStandard = (groupNo: number, value: unknown) => {
const nextGroupMap = { ...localPointGroupMap.value } const groupConfig = ensureGroupConfig(groupNo)
selectedPointIds.value.forEach(pointId => { const standard = Array.isArray(value) ? value.map(item => String(item || '').trim()).filter(Boolean) : []
nextGroupMap[pointId] = null localGroupConfigMap.value = {
}) ...localGroupConfigMap.value,
localPointGroupMap.value = nextGroupMap [groupNo]: {
...groupConfig,
standard
}
}
} }
const handleSubmit = () => { const handleSubmit = () => {
@@ -206,7 +382,9 @@ const handleSubmit = () => {
.summary-descriptions, .summary-descriptions,
.section-alert, .section-alert,
.point-table { .point-table,
.group-report-table,
.group-empty {
margin-top: 16px; margin-top: 16px;
} }
@@ -219,16 +397,23 @@ const handleSubmit = () => {
flex-wrap: wrap; flex-wrap: wrap;
} }
.secondary-toolbar {
margin-top: 20px;
}
.toolbar-title { .toolbar-title {
font-size: 15px; font-size: 15px;
font-weight: 600; font-weight: 600;
color: var(--el-text-color-primary); color: var(--el-text-color-primary);
} }
.toolbar-actions { .toolbar-tip {
display: flex; font-size: 12px;
align-items: center; color: var(--el-text-color-secondary);
gap: 12px; }
flex-wrap: wrap;
:deep(.el-select),
:deep(.el-input) {
width: 100%;
} }
</style> </style>

View File

@@ -59,6 +59,7 @@
:loading="detailLoading" :loading="detailLoading"
:detail="detailRecord" :detail="detailRecord"
:group-reports="detailGroupReports" :group-reports="detailGroupReports"
:client-unit-label-map="clientUnitLabelMap"
:company-label-map="companyLabelMap" :company-label-map="companyLabelMap"
:standard-label-map="standardLabelMap" :standard-label-map="standardLabelMap"
:downloading-group-report-id="downloadingGroupReportId" :downloading-group-report-id="downloadingGroupReportId"
@@ -78,6 +79,10 @@
:saving="groupDialogSaving" :saving="groupDialogSaving"
:record="currentGroupRecord" :record="currentGroupRecord"
:point-list="groupPointList" :point-list="groupPointList"
:group-reports="groupReportList"
:client-unit-options="clientUnitOptions"
:company-options="companyOptions"
:standard-options="standardOptions"
@submit="handleSaveGroups" @submit="handleSaveGroups"
/> />
</div> </div>
@@ -125,8 +130,6 @@ import {
normalizeReportTaskListParams, normalizeReportTaskListParams,
normalizeReportTaskPage, normalizeReportTaskPage,
normalizeReportTaskPointList, normalizeReportTaskPointList,
resolveReportTaskCreateUnitText,
resolveReportTaskStandardText,
resolveReportTaskText, resolveReportTaskText,
unwrapReportTaskPayload unwrapReportTaskPayload
} from './utils/testReport' } from './utils/testReport'
@@ -154,10 +157,12 @@ const detailGroupReports = ref<ReportTask.ReportTaskGroupReportRecord[]>([])
const currentAuditRecord = ref<ReportTask.ReportTaskRecord | null>(null) const currentAuditRecord = ref<ReportTask.ReportTaskRecord | null>(null)
const currentGroupRecord = ref<ReportTask.ReportTaskRecord | null>(null) const currentGroupRecord = ref<ReportTask.ReportTaskRecord | null>(null)
const groupPointList = ref<ReportTask.ReportTaskPointRecord[]>([]) const groupPointList = ref<ReportTask.ReportTaskPointRecord[]>([])
const groupReportList = ref<ReportTask.ReportTaskGroupReportRecord[]>([])
const clientUnitOptions = ref<Array<{ label: string; value: string }>>([]) const clientUnitOptions = ref<Array<{ label: string; value: string }>>([])
const reportModelOptions = ref<Array<{ label: string; value: string }>>([]) const reportModelOptions = ref<Array<{ label: string; value: string }>>([])
const downloadingGroupReportId = ref('') const downloadingGroupReportId = ref('')
const clientUnitLabelMap = computed(() => buildOptionLabelMap(clientUnitOptions.value))
const companyOptions = computed(() => buildDictSelectOptions(dictStore.getDictData(DICT_CODES.TEST_REPORT_COMPANY))) const companyOptions = computed(() => buildDictSelectOptions(dictStore.getDictData(DICT_CODES.TEST_REPORT_COMPANY)))
const standardOptions = computed(() => buildDictSelectOptions(dictStore.getDictData(DICT_CODES.TEST_REPORT_STANDARD))) const standardOptions = computed(() => buildDictSelectOptions(dictStore.getDictData(DICT_CODES.TEST_REPORT_STANDARD)))
const companyLabelMap = computed(() => buildOptionLabelMap(companyOptions.value)) const companyLabelMap = computed(() => buildOptionLabelMap(companyOptions.value))
@@ -180,7 +185,7 @@ const columns = reactive<ColumnProps<ReportTask.ReportTaskRecord>[]>([
el: 'input', el: 'input',
order: 1, order: 1,
props: { props: {
placeholder: '请输入报告编号、合同编号、委托单位或模板' placeholder: '请输入报告编号或模板'
} }
} }
}, },
@@ -202,14 +207,7 @@ const columns = reactive<ColumnProps<ReportTask.ReportTaskRecord>[]>([
} }
}, },
{ prop: 'no', label: '报告编号', minWidth: 180, fixed: 'left', render: ({ row }) => resolveReportTaskText(row.no) }, { prop: 'no', label: '报告编号', minWidth: 180, fixed: 'left', render: ({ row }) => resolveReportTaskText(row.no) },
{ prop: 'clientUnitName', label: '委托单位', minWidth: 160, render: ({ row }) => resolveReportTaskText(row.clientUnitName) },
{ prop: 'reportModelName', label: '报告模板', minWidth: 160, render: ({ row }) => resolveReportTaskText(row.reportModelName) }, { prop: 'reportModelName', label: '报告模板', minWidth: 160, render: ({ row }) => resolveReportTaskText(row.reportModelName) },
{
prop: 'createUnit',
label: '检测公司',
minWidth: 160,
render: ({ row }) => resolveReportTaskCreateUnitText(row.createUnit, companyLabelMap.value)
},
{ {
prop: 'state', prop: 'state',
label: '报告状态', label: '报告状态',
@@ -315,7 +313,6 @@ const openEditDialog = async (row: ReportTask.ReportTaskRecord) => {
formMode.value = 'edit' formMode.value = 'edit'
try { try {
// 关键业务节点:编辑表单提交仍依赖原始 clientUnitId、reportModelId、createUnit、standard 等字段,不能只回填列表展示文本。
const response = await getReportTaskDetailApi(row.id) const response = await getReportTaskDetailApi(row.id)
currentRecord.value = unwrapReportTaskPayload<ReportTask.ReportTaskRecord>(response) currentRecord.value = unwrapReportTaskPayload<ReportTask.ReportTaskRecord>(response)
formDialogVisible.value = true formDialogVisible.value = true
@@ -351,6 +348,7 @@ const openDetailDialog = async (row: ReportTask.ReportTaskRecord) => {
return return
} }
await ensureFormOptionsReady()
detailRecord.value = null detailRecord.value = null
detailGroupReports.value = [] detailGroupReports.value = []
detailDialogVisible.value = true detailDialogVisible.value = true
@@ -378,15 +376,21 @@ const openGroupDialog = async (row: ReportTask.ReportTaskRecord) => {
currentGroupRecord.value = row currentGroupRecord.value = row
groupPointList.value = [] groupPointList.value = []
groupReportList.value = []
groupDialogVisible.value = true groupDialogVisible.value = true
groupDialogLoading.value = true groupDialogLoading.value = true
try { try {
const response = await listReportTaskPointsApi(row.id) await ensureFormOptionsReady()
groupPointList.value = normalizeReportTaskPointList(response) const [pointResponse, groupReportResponse] = await Promise.all([
listReportTaskPointsApi(row.id),
listReportTaskGroupReportsApi(row.id)
])
groupPointList.value = normalizeReportTaskPointList(pointResponse)
groupReportList.value = normalizeReportTaskGroupReportList(groupReportResponse)
} catch (error) { } catch (error) {
groupDialogVisible.value = false groupDialogVisible.value = false
ElMessage.error(getReportTaskErrorMessage(error, '监测点列表查询失败')) ElMessage.error(getReportTaskErrorMessage(error, '监测点分组数据查询失败'))
} finally { } finally {
groupDialogLoading.value = false groupDialogLoading.value = false
} }
@@ -565,9 +569,9 @@ const handleExport = async () => {
onMounted(async () => { onMounted(async () => {
try { try {
await ensureReportTaskDictOptionsReady() await ensureFormOptionsReady()
} catch (error) { } catch (error) {
ElMessage.error(getReportTaskErrorMessage(error, '报告任务字典加载失败')) ElMessage.error(getReportTaskErrorMessage(error, '报告任务关联选项加载失败'))
} }
}) })
</script> </script>

View File

@@ -33,8 +33,10 @@ export const REPORT_TASK_GENERATE_STATE_MAP: Record<
{ text: string; tagType: TagType } { text: string; tagType: TagType }
> = { > = {
0: { text: '未生成', tagType: 'info' }, 0: { text: '未生成', tagType: 'info' },
1: { text: '生成中', tagType: 'warning' }, 1: { text: '部分成功', tagType: 'warning' },
2: { text: '已生成', tagType: 'success' } 2: { text: '全部成功', tagType: 'success' },
3: { text: '生成中', tagType: 'warning' },
4: { text: '生成失败', tagType: 'danger' }
} }
export const REPORT_TASK_GROUP_REPORT_GENERATE_STATUS_TEXT_MAP: Record<number, string> = { export const REPORT_TASK_GROUP_REPORT_GENERATE_STATUS_TEXT_MAP: Record<number, string> = {
0: '未生成', 0: '未生成',
@@ -249,7 +251,12 @@ export const normalizeReportTaskGroupReportList = (
response as ResultData<ReportTask.ReportTaskGroupReportRecord[]> response as ResultData<ReportTask.ReportTaskGroupReportRecord[]>
) )
return Array.isArray(payload) ? payload : [] return Array.isArray(payload)
? payload.map(item => ({
...item,
standard: Array.isArray(item?.standard) ? item.standard : parseReportTaskStandardIds(item?.standard)
}))
: []
} }
export const normalizeReportTaskLedgerSnapshot = ( export const normalizeReportTaskLedgerSnapshot = (
@@ -391,7 +398,18 @@ export const stringifyReportTaskStandardIds = (value: string[]) =>
export const normalizeReportTaskGroupSavePayload = ( export const normalizeReportTaskGroupSavePayload = (
pointList: ReportTask.ReportTaskPointRecord[], pointList: ReportTask.ReportTaskPointRecord[],
pointGroupMap: Record<string, number | string | null | undefined> pointGroupMap: Record<string, number | string | null | undefined>,
groupConfigMap: Record<
number,
{
reportNo?: string
clientUnitId?: string
createUnit?: string
contractNumber?: string
standard?: string[]
phonenumber?: string
}
> = {}
): ReportTask.ReportTaskGroupSaveRequest => { ): ReportTask.ReportTaskGroupSaveRequest => {
const groupedPointMap = new Map<number, string[]>() const groupedPointMap = new Map<number, string[]>()
@@ -413,7 +431,15 @@ export const normalizeReportTaskGroupSavePayload = (
.sort(([groupNoA], [groupNoB]) => groupNoA - groupNoB) .sort(([groupNoA], [groupNoB]) => groupNoA - groupNoB)
.map(([groupNo, pointIds]) => ({ .map(([groupNo, pointIds]) => ({
groupNo, groupNo,
pointIds pointIds,
reportNo: String(groupConfigMap[groupNo]?.reportNo || '').trim() || undefined,
clientUnitId: String(groupConfigMap[groupNo]?.clientUnitId || '').trim() || undefined,
createUnit: String(groupConfigMap[groupNo]?.createUnit || '').trim() || undefined,
contractNumber: String(groupConfigMap[groupNo]?.contractNumber || '').trim() || undefined,
standard: Array.isArray(groupConfigMap[groupNo]?.standard)
? groupConfigMap[groupNo].standard!.map(item => String(item || '').trim()).filter(Boolean)
: [],
phonenumber: String(groupConfigMap[groupNo]?.phonenumber || '').trim() || undefined
})) }))
} }
} }

View File

@@ -1,29 +1,19 @@
import type { ReportTask } from '@/api/aireport/testReport/interface' import type { ReportTask } from '@/api/aireport/testReport/interface'
import { generateUUID } from '@/utils' import { generateUUID } from '@/utils'
import { normalizeReportTaskLedgerSnapshot, parseReportTaskStandardIds, stringifyReportTaskStandardIds } from './testReport' import { normalizeReportTaskLedgerSnapshot } from './testReport'
export interface ReportTaskFormModel { export interface ReportTaskFormModel {
id: string id: string
no: string no: string
clientUnitId: string
reportModelId: string reportModelId: string
createUnit: string
contractNumber: string
standardIds: string[]
data: string data: string
phonenumber: string
} }
export const createDefaultReportTaskFormModel = (): ReportTaskFormModel => ({ export const createDefaultReportTaskFormModel = (): ReportTaskFormModel => ({
id: '', id: '',
no: '', no: '',
clientUnitId: '',
reportModelId: '', reportModelId: '',
createUnit: '', data: '{}'
contractNumber: '',
standardIds: [],
data: '{}',
phonenumber: ''
}) })
export const buildReportTaskDraftId = (mode: 'create' | 'edit', record: ReportTask.ReportTaskRecord | null) => { export const buildReportTaskDraftId = (mode: 'create' | 'edit', record: ReportTask.ReportTaskRecord | null) => {
@@ -40,13 +30,8 @@ export const buildReportTaskFormContext = (mode: 'create' | 'edit', record: Repo
formModel.id = record?.id || '' formModel.id = record?.id || ''
formModel.no = record?.no || '' formModel.no = record?.no || ''
formModel.clientUnitId = record?.clientUnitId || ''
formModel.reportModelId = record?.reportModelId || '' formModel.reportModelId = record?.reportModelId || ''
formModel.createUnit = record?.createUnit || '' formModel.data = typeof record?.data === 'string' ? record.data : JSON.stringify(record?.data || {})
formModel.contractNumber = record?.contractNumber || ''
formModel.standardIds = parseReportTaskStandardIds(record?.standard)
formModel.data = record?.data || '{}'
formModel.phonenumber = record?.phonenumber || ''
return { return {
formModel, formModel,
@@ -62,11 +47,6 @@ export const buildReportTaskSavePayload = (
): ReportTask.ReportTaskSaveRequest => ({ ): ReportTask.ReportTaskSaveRequest => ({
id: draftId || formModel.id || undefined, id: draftId || formModel.id || undefined,
no: formModel.no.trim(), no: formModel.no.trim(),
clientUnitId: formModel.clientUnitId,
reportModelId: formModel.reportModelId, reportModelId: formModel.reportModelId,
createUnit: formModel.createUnit, data: JSON.parse(formModel.data.trim() || '{}')
contractNumber: formModel.contractNumber.trim(),
standard: stringifyReportTaskStandardIds(formModel.standardIds),
data: JSON.parse(formModel.data.trim() || '{}'),
phonenumber: formModel.phonenumber.trim() || undefined
}) })

View File

@@ -96,18 +96,6 @@
<el-input v-model="item.no" maxlength="32" clearable placeholder="请输入报告编号" /> <el-input v-model="item.no" maxlength="32" clearable placeholder="请输入报告编号" />
</el-form-item> </el-form-item>
</el-col> </el-col>
<el-col :xs="24" :sm="12" :md="8">
<el-form-item label="委托单位" required>
<el-select v-model="item.clientUnitId" filterable clearable placeholder="请选择委托单位">
<el-option
v-for="option in clientUnitOptions"
:key="option.value"
:label="option.label"
:value="option.value"
/>
</el-select>
</el-form-item>
</el-col>
<el-col :xs="24" :sm="12" :md="8"> <el-col :xs="24" :sm="12" :md="8">
<el-form-item label="报告模板" required> <el-form-item label="报告模板" required>
<el-select v-model="item.reportModelId" filterable clearable placeholder="请选择报告模板"> <el-select v-model="item.reportModelId" filterable clearable placeholder="请选择报告模板">
@@ -120,47 +108,6 @@
</el-select> </el-select>
</el-form-item> </el-form-item>
</el-col> </el-col>
<el-col :xs="24" :sm="12" :md="8">
<el-form-item label="检测公司" required>
<el-select v-model="item.createUnit" filterable clearable placeholder="请选择检测公司">
<el-option
v-for="option in companyOptions"
:key="option.value"
:label="option.label"
:value="option.value"
/>
</el-select>
</el-form-item>
</el-col>
<el-col :xs="24" :sm="12" :md="8">
<el-form-item label="合同编号" required>
<el-input v-model="item.contractNumber" maxlength="32" clearable placeholder="请输入合同编号" />
</el-form-item>
</el-col>
<el-col :xs="24" :sm="12" :md="8">
<el-form-item label="联系电话">
<el-input v-model="item.phonenumber" maxlength="32" clearable placeholder="请输入联系电话" />
</el-form-item>
</el-col>
<el-col :xs="24">
<el-form-item label="检测依据" required>
<el-select
v-model="item.standard"
multiple
filterable
clearable
collapse-tags
placeholder="请选择检测依据"
>
<el-option
v-for="option in standardOptions"
:key="option.value"
:label="option.label"
:value="option.value"
/>
</el-select>
</el-form-item>
</el-col>
</el-row> </el-row>
</el-form> </el-form>
</div> </div>
@@ -202,10 +149,7 @@ const props = defineProps<{
zipValidating: boolean zipValidating: boolean
createSaving: boolean createSaving: boolean
validateResult: ReportTaskMulti.ReportTaskMultiValidateResult | null validateResult: ReportTaskMulti.ReportTaskMultiValidateResult | null
clientUnitOptions: SelectOption[]
reportModelOptions: SelectOption[] reportModelOptions: SelectOption[]
companyOptions: SelectOption[]
standardOptions: SelectOption[]
}>() }>()
const emit = defineEmits<{ const emit = defineEmits<{
@@ -252,12 +196,7 @@ const buildChildConfigForms = (templateDirs: ReportTaskMulti.ReportTaskMultiTemp
templateDirName: String(item.dirName || ''), templateDirName: String(item.dirName || ''),
templateDirPath: String(item.dirPath || ''), templateDirPath: String(item.dirPath || ''),
no: '', no: '',
clientUnitId: '',
reportModelId: '', reportModelId: '',
createUnit: '',
contractNumber: '',
standard: [],
phonenumber: '',
pointCount: Number(item.pointCount || 0), pointCount: Number(item.pointCount || 0),
groupCount: Number(item.groupCount || 0), groupCount: Number(item.groupCount || 0),
excelAttachmentCount: Number(item.excelAttachmentCount || 0), excelAttachmentCount: Number(item.excelAttachmentCount || 0),
@@ -313,11 +252,7 @@ const validateCreateForm = () => {
for (const item of createForm.childReports) { for (const item of createForm.childReports) {
if (!item.no.trim()) return `模板目录 ${item.templateDirName} 缺少报告编号` if (!item.no.trim()) return `模板目录 ${item.templateDirName} 缺少报告编号`
if (!item.clientUnitId) return `模板目录 ${item.templateDirName} 缺少委托单位`
if (!item.reportModelId) return `模板目录 ${item.templateDirName} 缺少报告模板` if (!item.reportModelId) return `模板目录 ${item.templateDirName} 缺少报告模板`
if (!item.createUnit) return `模板目录 ${item.templateDirName} 缺少检测公司`
if (!item.contractNumber.trim()) return `模板目录 ${item.templateDirName} 缺少合同编号`
if (!item.standard.length) return `模板目录 ${item.templateDirName} 缺少检测依据`
} }
return '' return ''
@@ -337,12 +272,7 @@ const handleCreate = () => {
templateDirName: item.templateDirName, templateDirName: item.templateDirName,
templateDirPath: item.templateDirPath, templateDirPath: item.templateDirPath,
no: item.no.trim(), no: item.no.trim(),
clientUnitId: item.clientUnitId, reportModelId: item.reportModelId
reportModelId: item.reportModelId,
createUnit: item.createUnit,
contractNumber: item.contractNumber.trim(),
standard: item.standard,
phonenumber: item.phonenumber?.trim() || undefined
})) }))
}) })
} }

View File

@@ -17,10 +17,7 @@
:zip-validating="zipValidating" :zip-validating="zipValidating"
:create-saving="createSaving" :create-saving="createSaving"
:validate-result="zipValidateResult" :validate-result="zipValidateResult"
:client-unit-options="clientUnitOptions"
:report-model-options="reportModelOptions" :report-model-options="reportModelOptions"
:company-options="companyOptions"
:standard-options="standardOptions"
@validate-zip="handleValidateZip" @validate-zip="handleValidateZip"
@create="handleCreateMulti" @create="handleCreateMulti"
@reset="resetCreateDialogState" @reset="resetCreateDialogState"
@@ -96,6 +93,7 @@
:loading="childDetailLoading" :loading="childDetailLoading"
:detail="childDetailRecord" :detail="childDetailRecord"
:group-reports="childDetailGroupReports" :group-reports="childDetailGroupReports"
:client-unit-label-map="clientUnitLabelMap"
:company-label-map="companyLabelMap" :company-label-map="companyLabelMap"
:standard-label-map="standardLabelMap" :standard-label-map="standardLabelMap"
:downloading-group-report-id="downloadingGroupReportId" :downloading-group-report-id="downloadingGroupReportId"
@@ -108,6 +106,10 @@
:saving="childGroupSaving" :saving="childGroupSaving"
:record="currentChildGroupRecord" :record="currentChildGroupRecord"
:point-list="childGroupPointList" :point-list="childGroupPointList"
:group-reports="childGroupReports"
:client-unit-options="clientUnitOptions"
:company-options="companyOptions"
:standard-options="standardOptions"
@submit="handleSaveChildGroups" @submit="handleSaveChildGroups"
/> />
</div> </div>
@@ -192,6 +194,7 @@ const detailRecord = ref<ReportTaskMulti.ReportTaskMultiRecord | null>(null)
const detailChildReports = ref<ReportTask.ReportTaskRecord[]>([]) const detailChildReports = ref<ReportTask.ReportTaskRecord[]>([])
const childDetailRecord = ref<ReportTask.ReportTaskRecord | null>(null) const childDetailRecord = ref<ReportTask.ReportTaskRecord | null>(null)
const childDetailGroupReports = ref<ReportTask.ReportTaskGroupReportRecord[]>([]) const childDetailGroupReports = ref<ReportTask.ReportTaskGroupReportRecord[]>([])
const childGroupReports = ref<ReportTask.ReportTaskGroupReportRecord[]>([])
const downloadingMultiId = ref('') const downloadingMultiId = ref('')
const downloadingGroupReportId = ref('') const downloadingGroupReportId = ref('')
const currentChildGroupRecord = ref<ReportTask.ReportTaskRecord | null>(null) const currentChildGroupRecord = ref<ReportTask.ReportTaskRecord | null>(null)
@@ -199,6 +202,7 @@ const childGroupPointList = ref<ReportTask.ReportTaskPointRecord[]>([])
const clientUnitOptions = ref<Array<{ label: string; value: string }>>([]) const clientUnitOptions = ref<Array<{ label: string; value: string }>>([])
const reportModelOptions = ref<Array<{ label: string; value: string }>>([]) const reportModelOptions = ref<Array<{ label: string; value: string }>>([])
const clientUnitLabelMap = computed(() => buildOptionLabelMap(clientUnitOptions.value))
const companyOptions = computed(() => buildDictSelectOptions(dictStore.getDictData(DICT_CODES.TEST_REPORT_COMPANY))) const companyOptions = computed(() => buildDictSelectOptions(dictStore.getDictData(DICT_CODES.TEST_REPORT_COMPANY)))
const standardOptions = computed(() => buildDictSelectOptions(dictStore.getDictData(DICT_CODES.TEST_REPORT_STANDARD))) const standardOptions = computed(() => buildDictSelectOptions(dictStore.getDictData(DICT_CODES.TEST_REPORT_STANDARD)))
const companyLabelMap = computed(() => buildOptionLabelMap(companyOptions.value)) const companyLabelMap = computed(() => buildOptionLabelMap(companyOptions.value))
@@ -415,6 +419,7 @@ const openChildDetailDialog = async (row: ReportTask.ReportTaskRecord) => {
childDetailDialogVisible.value = true childDetailDialogVisible.value = true
childDetailLoading.value = true childDetailLoading.value = true
try { try {
await ensureCreateOptionsReady()
const [detailResponse, groupReportsResponse] = await Promise.all([ const [detailResponse, groupReportsResponse] = await Promise.all([
getReportTaskDetailApi(reportId), getReportTaskDetailApi(reportId),
listReportTaskGroupReportsApi(reportId) listReportTaskGroupReportsApi(reportId)
@@ -439,14 +444,20 @@ const openChildGroupDialog = async (row: ReportTask.ReportTaskRecord) => {
} }
currentChildGroupRecord.value = row currentChildGroupRecord.value = row
childGroupPointList.value = [] childGroupPointList.value = []
childGroupReports.value = []
childGroupDialogVisible.value = true childGroupDialogVisible.value = true
childGroupLoading.value = true childGroupLoading.value = true
try { try {
const response = await listReportTaskPointsApi(reportId) await ensureCreateOptionsReady()
childGroupPointList.value = normalizeReportTaskPointList(response) const [pointResponse, groupReportResponse] = await Promise.all([
listReportTaskPointsApi(reportId),
listReportTaskGroupReportsApi(reportId)
])
childGroupPointList.value = normalizeReportTaskPointList(pointResponse)
childGroupReports.value = normalizeReportTaskGroupReportList(groupReportResponse)
} catch (error) { } catch (error) {
childGroupDialogVisible.value = false childGroupDialogVisible.value = false
ElMessage.error(getReportTaskErrorMessage(error, '子报告监测点查询失败')) ElMessage.error(getReportTaskErrorMessage(error, '子报告分组数据查询失败'))
} finally { } finally {
childGroupLoading.value = false childGroupLoading.value = false
} }
@@ -560,7 +571,7 @@ const handleBatchDelete = async (ids: string[]) => {
onMounted(async () => { onMounted(async () => {
try { try {
await ensureDictOptionsReady() await Promise.all([ensureDictOptionsReady(), loadClientUnitOptions()])
} catch (error) { } catch (error) {
ElMessage.error(getReportTaskErrorMessage(error, '字典加载失败')) ElMessage.error(getReportTaskErrorMessage(error, '字典加载失败'))
} }