feat(platform): filter quality issues by type
This commit is contained in:
@@ -4,7 +4,7 @@ import { useEffect, useState } from 'react';
|
||||
import { api } from '../api/client';
|
||||
import type { OpsHealth, QualitySummary, QualityIssueRow } from '../api/types';
|
||||
import { PageHeader } from '../components/PageHeader';
|
||||
import { qualityIssueLabel, qualityProtocolLabel, qualityProtocolOptions } from '../domain/qualityIssue';
|
||||
import { qualityIssueLabel, qualityIssueOptions, qualityProtocolLabel, qualityProtocolOptions } from '../domain/qualityIssue';
|
||||
import { qualityIssueVehicleLookup } from '../domain/vehicleLookup';
|
||||
|
||||
const statusColor: Record<string, 'green' | 'orange' | 'red' | 'grey'> = {
|
||||
@@ -39,6 +39,7 @@ function qualityParams(values: Record<string, string>) {
|
||||
const params = new URLSearchParams();
|
||||
if (values?.keyword) params.set('keyword', values.keyword);
|
||||
if (values?.protocol) params.set('protocol', values.protocol);
|
||||
if (values?.issueType) params.set('issueType', values.issueType);
|
||||
return params;
|
||||
}
|
||||
|
||||
@@ -71,6 +72,7 @@ export function Quality({
|
||||
const [loadingHealth, setLoadingHealth] = useState(true);
|
||||
const [filters, setFilters] = useState<Record<string, string>>({});
|
||||
const [pagination, setPagination] = useState({ currentPage: 1, pageSize: 20, total: 0 });
|
||||
const primaryIssueType = summary.issueTypes[0]?.name;
|
||||
|
||||
const loadIssues = (values: Record<string, string> = filters, page = pagination.currentPage, pageSize = pagination.pageSize) => {
|
||||
setLoadingIssues(true);
|
||||
@@ -111,6 +113,19 @@ export function Quality({
|
||||
loadHealth();
|
||||
}, []);
|
||||
|
||||
const applyFilters = (nextFilters: Record<string, string>) => {
|
||||
setFilters(nextFilters);
|
||||
loadSummary(nextFilters);
|
||||
loadIssues(nextFilters, 1, pagination.pageSize);
|
||||
};
|
||||
|
||||
const drillPrimaryIssue = () => {
|
||||
if (!primaryIssueType) {
|
||||
return;
|
||||
}
|
||||
applyFilters({ ...filters, issueType: primaryIssueType });
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="vp-page">
|
||||
<PageHeader title="质量治理" description="围绕车辆服务排查断链、VIN 缺失、字段缺失和链路健康" />
|
||||
@@ -121,12 +136,27 @@ export function Quality({
|
||||
{ label: '错误 / 警告', value: `${summary.errorCount}/${summary.warningCount}` },
|
||||
{
|
||||
label: '主要问题',
|
||||
value: summary.issueTypes.length > 0 ? `${qualityIssueLabel(summary.issueTypes[0].name)} ${summary.issueTypes[0].count}` : '-'
|
||||
value: primaryIssueType ? `${qualityIssueLabel(primaryIssueType)} ${summary.issueTypes[0].count}` : '-',
|
||||
onClick: primaryIssueType ? drillPrimaryIssue : undefined
|
||||
}
|
||||
].map((item) => (
|
||||
<Card key={item.label} bordered loading={loadingSummary}>
|
||||
<div className="vp-kpi-value">{item.value}</div>
|
||||
<div className="vp-kpi-label">{item.label}</div>
|
||||
{item.onClick ? (
|
||||
<button
|
||||
className="vp-result-summary-button"
|
||||
type="button"
|
||||
aria-label={`${item.label} ${item.value}`}
|
||||
onClick={item.onClick}
|
||||
>
|
||||
<div className="vp-kpi-value">{item.value}</div>
|
||||
<div className="vp-kpi-label">{item.label}</div>
|
||||
</button>
|
||||
) : (
|
||||
<>
|
||||
<div className="vp-kpi-value">{item.value}</div>
|
||||
<div className="vp-kpi-label">{item.label}</div>
|
||||
</>
|
||||
)}
|
||||
</Card>
|
||||
))}
|
||||
</div>
|
||||
@@ -141,11 +171,9 @@ export function Quality({
|
||||
</Col>
|
||||
</Row>
|
||||
<Card bordered title="质量问题" style={{ marginTop: 16 }}>
|
||||
<Form layout="horizontal" onSubmit={(values) => {
|
||||
<Form key={JSON.stringify(filters)} initValues={filters} layout="horizontal" onSubmit={(values) => {
|
||||
const nextFilters = values as Record<string, string>;
|
||||
setFilters(nextFilters);
|
||||
loadSummary(nextFilters);
|
||||
loadIssues(nextFilters, 1, pagination.pageSize);
|
||||
applyFilters(nextFilters);
|
||||
}} style={{ marginBottom: 12 }}>
|
||||
<Form.Input field="keyword" label="关键词" placeholder="VIN / 车牌 / 手机号 / 来源地址" style={{ width: 260 }} />
|
||||
<Form.Select field="protocol" label="数据来源" placeholder="全部来源" style={{ width: 160 }}>
|
||||
@@ -153,12 +181,15 @@ export function Quality({
|
||||
<Select.Option key={item.value} value={item.value}>{item.label}</Select.Option>
|
||||
))}
|
||||
</Form.Select>
|
||||
<Form.Select field="issueType" label="问题类型" placeholder="全部问题" style={{ width: 160 }}>
|
||||
{qualityIssueOptions.map((item) => (
|
||||
<Select.Option key={item.value} value={item.value}>{item.label}</Select.Option>
|
||||
))}
|
||||
</Form.Select>
|
||||
<Space>
|
||||
<Button htmlType="submit" theme="solid" type="primary">筛选</Button>
|
||||
<Button onClick={() => {
|
||||
setFilters({});
|
||||
loadSummary({});
|
||||
loadIssues({}, 1, pagination.pageSize);
|
||||
applyFilters({});
|
||||
}}>重置</Button>
|
||||
</Space>
|
||||
</Form>
|
||||
|
||||
Reference in New Issue
Block a user