diff --git a/.vscode/settings.json b/.vscode/settings.json index 33edcab..5c0f4e1 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -120,7 +120,7 @@ "source.organizeImports": "never", "source.fixAll.stylelint": "explicit" }, - "editor.defaultFormatter": "octref.vetur" + "editor.defaultFormatter": "stylelint.vscode-stylelint" }, "i18n-ally.localesPaths": ["src/locales/lang"], "i18n-ally.keystyle": "nested", diff --git a/src/api/alert/model/assessReport.ts b/src/api/alert/model/assessReport.ts index d840ad9..1c98aff 100644 --- a/src/api/alert/model/assessReport.ts +++ b/src/api/alert/model/assessReport.ts @@ -5,6 +5,8 @@ import type { AssessInitQuery, AssessInitResponse, AssessReportDetail, + AssessReportName, + AssessReportSimple, AssessReportSavePayload, } from './model/assessReportModel' import { defHttp } from '@/utils/http/axios' @@ -14,6 +16,9 @@ enum Api { REPORT = '/alert/assess-report/report', EVALUATE = '/alert/assess-report/evaluate', SAVE = '/alert/assess-report', + LEGACY_SAVE = '/alert/assess-report/legacy', + LIST = '/alert/assess-report/list', + NAME = '/alert/assess-report/name', CLEAN_SUMMARY = '/alert/assess-report/clean/summary', CLEAN_DEAD = '/alert/assess-report/clean/dead', CLEAN_CONDITION_RATE = '/alert/assess-report/clean/condition-rate', @@ -37,6 +42,19 @@ export function saveAssessReport(data: AssessReportSavePayload) { return defHttp.post({ url: Api.SAVE, data }) } +// 兼容旧表 Assess_Report_CFG 的插入 +export function saveAssessReportLegacy(data: AssessReportSavePayload & { rawReport?: string }) { + return defHttp.post({ url: Api.LEGACY_SAVE, data }) +} + +export function fetchAssessReportList(params: { modelId: number | string; version?: string; valid?: boolean }) { + return defHttp.get({ url: Api.LIST, params }) +} + +export function fetchAssessReportNames(params: { modelId: number | string; version?: string }) { + return defHttp.get({ url: Api.NAME, params }) +} + export function fetchCleanSummary(params: { modelId: number | string; time: string; version: string }) { return defHttp.get({ url: Api.CLEAN_SUMMARY, params }) } diff --git a/src/api/alert/model/model/assessReportModel.ts b/src/api/alert/model/model/assessReportModel.ts index 91e0da2..01a8311 100644 --- a/src/api/alert/model/model/assessReportModel.ts +++ b/src/api/alert/model/model/assessReportModel.ts @@ -121,3 +121,20 @@ export interface AssessCleanSummary { envelopeClauses?: string[] timeRange?: string[] } + +export interface AssessReportName { + id: number + name: string +} + +export interface AssessReportSimple { + id: number + score?: number + coverRange?: number + validFlag?: boolean + identifier?: string + verifier?: string + assessTime?: string + version?: string + conditionName?: string +} diff --git a/src/api/alert/model/models.ts b/src/api/alert/model/models.ts index d88f12e..c384a3a 100644 --- a/src/api/alert/model/models.ts +++ b/src/api/alert/model/models.ts @@ -11,12 +11,14 @@ enum Api { TRAIN_MODEL = '/alert/model/train', TEST_MODEL = '/alert/model/test', BOTTOM_MODEL = '/alert/model/bottom/', + VERSION_LIST = '/alert/model/version/', + VERSION_NEW = '/alert/model/version/new/', } export function modelCardListApi(params?: ModelQueryParams) { return defHttp.get({ url: Api.MODEL_CARD_LIST, params }) } -export const modelInfoApi = (id: any) => defHttp.get({ url: `${Api.MODEL_INFO}/${id}` }) +export const modelInfoApi = (idOrPath: any) => defHttp.get({ url: `${Api.MODEL_INFO}/${idOrPath}` }) export function updateModelInfo(params: any) { return defHttp.patch({ url: Api.MODEL_INFO, params }) @@ -39,4 +41,12 @@ export const trainModelApi = (params: any) => defHttp.post({ url: Api.TRAIN export const testModelApi = (params: any) => defHttp.post({ url: Api.TEST_MODEL, data: params }) -export const bottomModelApi = (id: any) => defHttp.post({ url: Api.BOTTOM_MODEL + id }) +export const bottomModelApi = (id: any, reportId?: number | string) => + defHttp.post({ + url: Api.BOTTOM_MODEL + id, + data: reportId ? { reportId } : {}, + }) + +export const versionListApi = (id: any) => defHttp.get({ url: Api.VERSION_LIST + id }) + +export const createDraftVersionApi = (id: any) => defHttp.post({ url: Api.VERSION_NEW + id }) diff --git a/src/views/model/AssessReport.vue b/src/views/model/AssessReport.vue index c1581ac..28ce133 100644 --- a/src/views/model/AssessReport.vue +++ b/src/views/model/AssessReport.vue @@ -25,7 +25,7 @@ import { fetchAssessCondition, fetchCleanSummary, fetchReportDetail, - saveAssessReport, + saveAssessReportLegacy, } from '@/api/alert/model/assessReport' import type { AssessCleanSummary, @@ -37,7 +37,8 @@ import type { import { PageWrapper } from '@/components/Page' import { useMessage } from '@/hooks/web/useMessage' import { useUserStore } from '@/store/modules/user' -import { modelInfoApi } from '@/api/alert/model/models' +import { modelInfoApi, versionListApi } from '@/api/alert/model/models' +import Icon from '@/components/Icon' type RangeValue = [Dayjs, Dayjs] @@ -98,13 +99,14 @@ export default defineComponent({ const assessResult = ref(defaultAssessResult()) const cleanSummary = ref() const cleanModalVisible = ref(false) + const selectedVersion = ref(((route.query.version as string) || 'v-test').replace('%20', ' ')) const formModel = reactive<{ version: string timeRange?: RangeValue description?: string }>({ - version: ((route.query.version as string) || 'v-test').replace('%20', ' '), + version: selectedVersion.value, timeRange: undefined, description: '', }) @@ -115,6 +117,123 @@ export default defineComponent({ percent: 15, }) + const basicColumns = computed[]>(() => [ + { + title: ' ', + dataIndex: 'group', + width: 120, + align: 'center', + customRender: ({ record }) => { + if (record.group === '模型参数') { + return { + children: record.group, + props: { rowSpan: record.groupRowSpan }, + } + } + return { + children: record.name ?? '', + props: { rowSpan: 1 }, + } + }, + }, + { + title: '序号', + dataIndex: 'displayIndex', + width: 80, + align: 'center', + customRender: ({ record }) => { + if (record.group === '模型信息') + return { children: '', props: { colSpan: 0 } } + + return record.displayIndex ?? record.index ?? '--' + }, + }, + { + title: '参数名称', + dataIndex: 'name', + width: 220, + align: 'center', + customRender: ({ record, text }) => { + if (record.group === '模型信息') + return { children: '', props: { colSpan: 0 } } + return record.description ?? record.name ?? text ?? '--' + }, + }, + { + title: '参数KKS码 / 内容', + dataIndex: 'contentOrPoint', + width: 200, + align: 'center', + customRender: ({ record }) => { + if (record.group === '模型参数') + return record.pointId ?? record.PointId ?? '--' + return { + children: record.content ?? '--', + props: { colSpan: 5 }, + } + }, + }, + { + title: '是否锁定', + dataIndex: 'lock', + width: 120, + align: 'center', + customRender: ({ record, text }) => { + if (record.group === '模型信息') + return { children: '', props: { colSpan: 0 } } + return text ? '是' : '否' + }, + }, + { + title: '是否参与预警', + dataIndex: 'alarm', + width: 160, + align: 'center', + customRender: ({ record }) => { + if (record.group === '模型信息') + return { children: '', props: { colSpan: 0 } } + return h(Switch, { + checked: record.alarm, + disabled: record.lock, + onChange: (checked: boolean) => toggleAlarm(record, checked), + }) + }, + }, + ]) + + const basicRows = computed(() => { + const paramRows = (pointRows.value || []).map((item, index) => ({ + ...item, + key: `param-${item.pointId || index}`, + group: '模型参数', + displayIndex: (item.index ?? index) + 1, + groupRowSpan: index === 0 ? (pointRows.value?.length || 0) : 0, + })) + const versionRow = { + name: '版本', + content: selectedVersion.value || 'v-test', + key: 'info-version', + group: '模型信息', + displayIndex: '--', + } + const infoRows = [versionRow].concat((modeRows.value || []).map((item, index) => ({ + ...item, + key: `info-${index}`, + group: '模型信息', + displayIndex: '--', + }))) + return [...paramRows, ...infoRows] + }) + + const sumTrainMode = (trainTime?: any[]) => { + if (!Array.isArray(trainTime)) + return undefined + return trainTime.reduce((sum, item) => { + const modeVal = Number(item?.mode) + return sum + (Number.isFinite(modeVal) ? modeVal : 0) + }, 0) + } + const normalizePointRow = (item: any, index: number): AssessPointRow => { const tMax = item.TMax ?? item.tMax const tMin = item.TMin ?? item.tMin @@ -193,16 +312,18 @@ export default defineComponent({ timestamp.value = detail.report.time || '' } else { - const modelInfo = await modelInfoApi(modelId.value as string) + const modelInfo = await modelInfoApi(`${modelId.value}?version=${encodeURIComponent(formModel.version)}`) baseInfo.value = modelInfo || {} modelName.value = modelInfo?.name || modelInfo?.Model_Name || '' + selectedVersion.value = modelInfo?.version || modelInfo?.Cur_Version || formModel.version pointRows.value = (modelInfo?.pointInfo || []).map((item: any, index: number) => normalizePointRow(item, index)) + const sampleCount = sumTrainMode(modelInfo?.trainTime) modeRows.value = [ { name: '主元个数', content: modelInfo?.principal ?? '' }, { name: '运行模式', content: modelInfo?.alarmmodelset?.alarmname || '全工况运行' }, { name: '模式编码', content: modelInfo?.alarmmodelset?.alarmcondition || '1=1' }, { name: '样本类型', content: '训练样本' }, - { name: '样本数量', content: modelInfo?.trainTime?.length || '--' }, + { name: '样本数量', content: sampleCount ?? '--' }, ] assessRows.value = pointRows.value @@ -212,12 +333,8 @@ export default defineComponent({ bottomScore.value = modelInfo?.model_score coverage.value = modelInfo?.load_coverage timestamp.value = dayjs().format('YYYY-MM-DD HH:mm:ss') - if (!identifier.value) { - identifier.value = userStore.getUserInfo?.username - || userStore.getUserInfo?.nickName - || userStore.getUserInfo?.userName - || '' - } + if (!identifier.value) + identifier.value = userStore.getUserInfo?.user?.nickname } if (algorithm.value) { const condition = await fetchAssessCondition(algorithm.value) @@ -233,6 +350,7 @@ export default defineComponent({ } } + const toggleAlarm = (row: AssessPointRow, checked: boolean) => { row.alarm = checked assessRows.value = assessRows.value.filter(item => item.pointId !== row.pointId) @@ -328,7 +446,7 @@ export default defineComponent({ const limit: number[] = [] const uplow: string[] = [] points.forEach((p) => { - dead.push(p.dead ? 0 : 1) + dead.push(p.dead ? 1 : 0) limit.push(p.limit ? 1 : 0) uplow.push(`${p.Lower ?? p.lower ?? ''},${p.Upper ?? p.upper ?? ''}`) }) @@ -420,10 +538,10 @@ export default defineComponent({ const match = item.pointId === result.pointId || item.index === result.index if (!match) return item - const fdrForward = (result.fdrForward * 100).toFixed(2) - const fdrReverse = (result.fdrReverse * 100).toFixed(2) - const farForward = (result.farForward * 100).toFixed(2) - const farReverse = (result.farReverse * 100).toFixed(2) + const fdrForward = (Number(result.fdrForward) * 100).toFixed(2) + const fdrReverse = (Number(result.fdrReverse) * 100).toFixed(2) + const farForward = (Number(result.farForward) * 100).toFixed(2) + const farReverse = (Number(result.farReverse) * 100).toFixed(2) const reconForward = result.reconForward !== undefined ? result.reconForward.toFixed(2) : '--' const reconReverse = result.reconReverse !== undefined ? result.reconReverse.toFixed(2) : '--' return { @@ -435,7 +553,7 @@ export default defineComponent({ }) }) } - updateAssessResult(res?.durationSeconds, res?.coverage) + updateAssessResult(res?.durationSeconds ?? 0, res?.coverage ?? 0) if (!timestamp.value) timestamp.value = dayjs().format('YYYY-MM-DD HH:mm:ss') @@ -480,7 +598,11 @@ export default defineComponent({ timestamp: timestamp.value || dayjs().format('YYYY-MM-DD HH:mm:ss'), conditionName: modeRows.value[1]?.content as string, } - await saveAssessReport(payload) + // 仅调用 legacy 接口写入旧表 Assess_Report_CFG + await saveAssessReportLegacy({ + ...payload, + rawReport: JSON.stringify(payload.report), + }) createMessage.success('评估报告已提交') router.back() } @@ -511,44 +633,6 @@ export default defineComponent({ } } - const pointColumns = computed[]>(() => [ - { - title: ' ', - dataIndex: 'group', - width: 120, - align: 'center', - customRender: () => '模型参数', - }, - { - title: '序号', - dataIndex: 'index', - width: 80, - align: 'center', - customRender: ({ record, index }) => (record.index ?? index) + 1, - }, - { title: '参数名称', dataIndex: 'description', width: 220, align: 'center' }, - { title: '参数KKS码', dataIndex: 'pointId', width: 200, align: 'center' }, - { - title: '是否锁定', - dataIndex: 'lock', - width: 120, - align: 'center', - customRender: ({ text }) => (text ? '是' : '否'), - }, - { - title: '是否参与预警', - dataIndex: 'alarm', - width: 160, - align: 'center', - customRender: ({ record }) => - h(Switch, { - checked: record.alarm, - disabled: record.lock, - onChange: (checked: boolean) => toggleAlarm(record, checked), - }), - }, - ]) - const assessColumns = computed[]>(() => [ { title: '参与预警的参数', dataIndex: 'description', width: 200, align: 'center' }, { title: '单位', dataIndex: 'unit', width: 80, align: 'center' }, @@ -616,7 +700,9 @@ export default defineComponent({ loading, modeRows, modelName, - pointColumns, + selectedVersion, + basicColumns, + basicRows, pointRows, toggleAlarm, updateAssessResult, @@ -635,20 +721,12 @@ export default defineComponent({ 1. 模型基本信息 - diff --git a/src/views/model/list/ModelCard.vue b/src/views/model/list/ModelCard.vue index 4d2e898..4eca609 100644 --- a/src/views/model/list/ModelCard.vue +++ b/src/views/model/list/ModelCard.vue @@ -26,8 +26,9 @@ const [registerDraw, { openDrawer }] = useDrawer() // 点击模型卡片跳转训练页面 const go = useGo() -function changeModel(id) { - go(`/model/train/${id}`) +function changeModel(id, version?) { + const versionParam = version ? `?version=${encodeURIComponent(version)}` : '' + go(`/model/train/${id}${versionParam}`) } const modelCardList = ref>([]) @@ -55,6 +56,7 @@ watch( const card: ModelItem = { id: modelCard.id, title: modelCard.name, + version: modelCard.version, icon: icons[modelCard.status], value: 1, total: 1, @@ -90,7 +92,7 @@ watch( :hoverable="true" :body-style="item.bodyStyle" :head-style="item.headStyle" - @click="changeModel(item.id)" + @click="changeModel(item.id, item.version)" >