feat(web): Vitest 测试基建与首批单测(#27)+ 公共格式化函数
This commit is contained in:
Generated
+1185
-1
File diff suppressed because it is too large
Load Diff
+6
-1
@@ -7,6 +7,7 @@
|
||||
"dev": "vite",
|
||||
"build": "tsc -b && vite build",
|
||||
"lint": "oxlint",
|
||||
"test": "vitest run",
|
||||
"preview": "vite preview"
|
||||
},
|
||||
"dependencies": {
|
||||
@@ -24,12 +25,16 @@
|
||||
"react-router-dom": "^7.18.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@testing-library/jest-dom": "^7.0.1",
|
||||
"@testing-library/react": "^16.3.2",
|
||||
"@types/node": "^24.13.2",
|
||||
"@types/react": "^18.3.27",
|
||||
"@types/react-dom": "^18.3.7",
|
||||
"@vitejs/plugin-react": "^6.0.3",
|
||||
"jsdom": "^30.0.1",
|
||||
"oxlint": "^1.71.0",
|
||||
"typescript": "~6.0.2",
|
||||
"vite": "^8.1.1"
|
||||
"vite": "^8.1.1",
|
||||
"vitest": "^4.1.10"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,17 +3,13 @@ import { Drawer, Image, Tag, Typography } from 'antd';
|
||||
import { ProTable, type ProColumns } from '@ant-design/pro-components';
|
||||
import dayjs from 'dayjs';
|
||||
import { listInspections, type AIDetection, type InspectionRecord } from '../dal/inspections';
|
||||
import { aiClassLabel, riskLevelLabel } from '../utils/format';
|
||||
|
||||
const CLASS_LABELS: Record<string, string> = {
|
||||
healthy: '健康',
|
||||
sick: '疑似异常',
|
||||
};
|
||||
|
||||
const RISK_LABELS: Record<string, { color: string; text: string }> = {
|
||||
green: { color: 'green', text: '绿' },
|
||||
yellow: { color: 'gold', text: '黄' },
|
||||
orange: { color: 'orange', text: '橙' },
|
||||
red: { color: 'red', text: '红' },
|
||||
const RISK_LABELS: Record<string, { color: string }> = {
|
||||
green: { color: 'green' },
|
||||
yellow: { color: 'gold' },
|
||||
orange: { color: 'orange' },
|
||||
red: { color: 'red' },
|
||||
};
|
||||
|
||||
const aiClassOf = (rec: InspectionRecord) =>
|
||||
@@ -36,7 +32,7 @@ export default function InspectionsPage() {
|
||||
render: (_, r) => {
|
||||
const c = aiClassOf(r);
|
||||
return c ? (
|
||||
<Tag color={c === 'sick' ? 'red' : 'green'}>{CLASS_LABELS[c]}</Tag>
|
||||
<Tag color={c === 'sick' ? 'red' : 'green'}>{aiClassLabel(c)}</Tag>
|
||||
) : (
|
||||
'-'
|
||||
);
|
||||
@@ -50,7 +46,7 @@ export default function InspectionsPage() {
|
||||
const l = RISK_LABELS[r.riskLevel || ''] || RISK_LABELS.green;
|
||||
return r.riskScore !== undefined ? (
|
||||
<Tag color={l.color}>
|
||||
{l.text}({Math.round(r.riskScore!)} 分)
|
||||
{riskLevelLabel(r.riskLevel || '')}({Math.round(r.riskScore!)} 分)
|
||||
</Tag>
|
||||
) : (
|
||||
'-'
|
||||
@@ -106,13 +102,13 @@ export default function InspectionsPage() {
|
||||
状态:{detail.aiStatus === 'done' ? '检测成功' : '检测失败'}
|
||||
<br />
|
||||
风险分:{detail.riskScore !== undefined ? Math.round(detail.riskScore) : '-'}(
|
||||
{detail.riskLevel ? RISK_LABELS[detail.riskLevel]?.text || detail.riskLevel : '-'})
|
||||
{detail.riskLevel ? riskLevelLabel(detail.riskLevel) : '-'})
|
||||
</Typography.Paragraph>
|
||||
<Typography.Title level={5}>检测结果</Typography.Title>
|
||||
{detail.detections && detail.detections.length > 0 ? (
|
||||
detail.detections.map((d: AIDetection, i: number) => (
|
||||
<div key={i} style={{ padding: '6px 0', borderBottom: '1px solid #f0f0f0' }}>
|
||||
<Tag color={d.class === 'healthy' ? 'green' : 'red'}>{CLASS_LABELS[d.class] || d.class}</Tag>
|
||||
<Tag color={d.class === 'healthy' ? 'green' : 'red'}>{aiClassLabel(d.class)}</Tag>
|
||||
<span>置信度 {(d.confidence * 100).toFixed(1)}%</span>
|
||||
<span style={{ marginLeft: 12, color: '#999' }}>
|
||||
框 x:{Math.round(d.bbox.x)} y:{Math.round(d.bbox.y)} w:{Math.round(d.bbox.w)} h:{Math.round(d.bbox.h)}
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
import { describe, expect, it } from 'vitest';
|
||||
import { aiClassLabel, riskLevelLabel } from './format';
|
||||
|
||||
describe('aiClassLabel', () => {
|
||||
it('映射 healthy/sick 为中文', () => {
|
||||
expect(aiClassLabel('healthy')).toBe('健康');
|
||||
expect(aiClassLabel('sick')).toBe('疑似异常');
|
||||
});
|
||||
it('未知类别原样返回', () => {
|
||||
expect(aiClassLabel('unknown')).toBe('unknown');
|
||||
});
|
||||
});
|
||||
|
||||
describe('riskLevelLabel', () => {
|
||||
it('映射四级风险', () => {
|
||||
expect(riskLevelLabel('green')).toBe('绿');
|
||||
expect(riskLevelLabel('yellow')).toBe('黄');
|
||||
expect(riskLevelLabel('orange')).toBe('橙');
|
||||
expect(riskLevelLabel('red')).toBe('红');
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,15 @@
|
||||
const AI_CLASS_LABELS: Record<string, string> = {
|
||||
healthy: '健康',
|
||||
sick: '疑似异常',
|
||||
};
|
||||
|
||||
const RISK_LEVEL_LABELS: Record<string, string> = {
|
||||
green: '绿',
|
||||
yellow: '黄',
|
||||
orange: '橙',
|
||||
red: '红',
|
||||
};
|
||||
|
||||
export const aiClassLabel = (cls: string) => AI_CLASS_LABELS[cls] || cls;
|
||||
|
||||
export const riskLevelLabel = (level: string) => RISK_LEVEL_LABELS[level] || level;
|
||||
+4
-1
@@ -1,4 +1,4 @@
|
||||
import { defineConfig } from 'vite';
|
||||
import { defineConfig } from 'vitest/config';
|
||||
import react from '@vitejs/plugin-react';
|
||||
|
||||
export default defineConfig({
|
||||
@@ -27,6 +27,9 @@ export default defineConfig({
|
||||
},
|
||||
},
|
||||
},
|
||||
test: {
|
||||
environment: 'jsdom',
|
||||
},
|
||||
server: {
|
||||
host: '0.0.0.0',
|
||||
port: 5174,
|
||||
|
||||
Reference in New Issue
Block a user