feat(faults): add severity dimension and detail column
ci/woodpecker/push/woodpecker Pipeline was successful
ci/woodpecker/push/woodpecker Pipeline was successful
This commit is contained in:
@@ -43,7 +43,7 @@ export function FaultAnalysisModal({ metric, onClose, source }: { metric: FaultO
|
|||||||
}, [metric, revision, source?.url]);
|
}, [metric, revision, source?.url]);
|
||||||
const records = useMemo(() => data?.rows || [], [data]);
|
const records = useMemo(() => data?.rows || [], [data]);
|
||||||
const handlerLabels = useMemo(() => new Map(records.flatMap(row => row.handlers.map(h => [h.value, h.label] as const))), [records]);
|
const handlerLabels = useMemo(() => new Map(records.flatMap(row => row.handlers.map(h => [h.value, h.label] as const))), [records]);
|
||||||
const label = (d: FaultAnalysisDimension, value: string) => d === 'plate' ? value : d === 'faultStatus' ? FAULT_CLOSURE_LABELS[value] || value : d === 'handler' ? handlerLabels.get(value) || '未指派' : d === 'customer' ? data?.customerLabels[value] || '未关联客户' : getDimensionValueLabel(d, value);
|
const label = (d: FaultAnalysisDimension, value: string) => d === 'plate' ? value : d === 'faultStatus' ? FAULT_CLOSURE_LABELS[value] || value : d === 'handler' ? handlerLabels.get(value) || '未指派' : d === 'level' ? value || '未标注' : d === 'customer' ? data?.customerLabels[value] || '未关联客户' : getDimensionValueLabel(d, value);
|
||||||
const options = available.map(key => {
|
const options = available.map(key => {
|
||||||
const otherFilters = { ...filters }; delete otherFilters[key];
|
const otherFilters = { ...filters }; delete otherFilters[key];
|
||||||
const counts = new Map<string, number>();
|
const counts = new Map<string, number>();
|
||||||
@@ -78,7 +78,7 @@ export function FaultAnalysisModal({ metric, onClose, source }: { metric: FaultO
|
|||||||
<div className="analysis-scroll">
|
<div className="analysis-scroll">
|
||||||
{error ? <div className="fault-message" role="alert">{error}<button onClick={() => setRevision(v => v + 1)}>重试</button></div> : !data ? <FaultAnalysisLoading /> : group ? <>
|
{error ? <div className="fault-message" role="alert">{error}<button onClick={() => setRevision(v => v + 1)}>重试</button></div> : !data ? <FaultAnalysisLoading /> : group ? <>
|
||||||
<div className="analysis-table-tools"><button className="analysis-button" onClick={() => { setGroup(null); setLimit(50); }}><ArrowLeft size={14} />返回分组</button><span>{groupLabel(group)} · {details.length.toLocaleString()} 单</span><label className="fault-detail-search"><Search size={14} /><input aria-label="搜索故障明细" placeholder="搜索单号、车牌、描述、处置人" value={search} onChange={e => { setSearch(e.target.value); setLimit(50); }} /></label></div>
|
<div className="analysis-table-tools"><button className="analysis-button" onClick={() => { setGroup(null); setLimit(50); }}><ArrowLeft size={14} />返回分组</button><span>{groupLabel(group)} · {details.length.toLocaleString()} 单</span><label className="fault-detail-search"><Search size={14} /><input aria-label="搜索故障明细" placeholder="搜索单号、车牌、描述、处置人" value={search} onChange={e => { setSearch(e.target.value); setLimit(50); }} /></label></div>
|
||||||
<div className="analysis-group-table is-merged fault-detail-table"><table><thead><tr>{['车牌 / 故障单号', '运维处置人', '故障描述', '状态', '提报时间', '解决时间', '运营城市', '客户 / 部门'].map(h => <th key={h}>{h}</th>)}</tr></thead><tbody>{details.slice(0, limit).map(row => <tr key={row.id}><td><strong>{row.plate || '未关联车辆'}</strong><small>{row.code || '未编号'}</small></td><td>{row.handlers.map(h => h.label).join('、') || '未指派'}</td><td>{row.description || '—'}</td><td>{row.status}</td><td>{row.reportedAt || '—'}</td><td>{row.archivedAt || '—'}</td><td>{row.city}</td><td>{row.customerLabel}<small>{row.department}</small></td></tr>)}</tbody></table></div>
|
<div className="analysis-group-table is-merged fault-detail-table"><table><thead><tr>{['车牌 / 故障单号', '故障程度', '运维处置人', '故障描述', '状态', '提报时间', '解决时间', '运营城市', '客户 / 部门'].map(h => <th key={h}>{h}</th>)}</tr></thead><tbody>{details.slice(0, limit).map(row => <tr key={row.id}><td><strong>{row.plate || '未关联车辆'}</strong><small>{row.code || '未编号'}</small></td><td>{row.level || '未标注'}</td><td>{row.handlers.map(h => h.label).join('、') || '未指派'}</td><td>{row.description || '—'}</td><td>{row.status}</td><td>{row.reportedAt || '—'}</td><td>{row.archivedAt || '—'}</td><td>{row.city}</td><td>{row.customerLabel}<small>{row.department}</small></td></tr>)}</tbody></table></div>
|
||||||
{!details.length && <p className="fault-empty">没有符合条件的故障明细</p>}
|
{!details.length && <p className="fault-empty">没有符合条件的故障明细</p>}
|
||||||
{details.length > limit && <button className="analysis-button fault-more" onClick={() => setLimit(v => v + 50)}>加载更多明细(已显示 {limit} / {details.length})</button>}
|
{details.length > limit && <button className="analysis-button fault-more" onClick={() => setLimit(v => v + 50)}>加载更多明细(已显示 {limit} / {details.length})</button>}
|
||||||
</> : <>
|
</> : <>
|
||||||
|
|||||||
@@ -50,7 +50,7 @@ export default function FaultManagement(){
|
|||||||
</section>
|
</section>
|
||||||
<div className="fd-today-stats"><span>实时统计</span><button onClick={()=>drill('今日新增','reported')}>今日新增 <b>{t?.todayReported}</b></button><button onClick={()=>drill('本日解决','closed')}>本日解决 <b>{t?.todayClosed}</b></button></div>
|
<div className="fd-today-stats"><span>实时统计</span><button onClick={()=>drill('今日新增','reported')}>今日新增 <b>{t?.todayReported}</b></button><button onClick={()=>drill('本日解决','closed')}>本日解决 <b>{t?.todayClosed}</b></button></div>
|
||||||
<div className="fd-insight"><Info size={18}/><strong>{data.distributions.region[0]?`本期关注:${data.distributions.region[0].label}故障量最高,共 ${data.distributions.region[0].reported} 单;当前 ${t?.overdue} 单在办故障已逾期。`:'所选条件下暂无本期故障,可调整日期或筛选范围。'}</strong><span>按数量描述,不代表故障发生率。</span><button disabled={!t?.open} onClick={showAllOpen}>查看关联故障 <ArrowUpRight size={13}/></button></div>
|
<div className="fd-insight"><Info size={18}/><strong>{data.distributions.region[0]?`本期关注:${data.distributions.region[0].label}故障量最高,共 ${data.distributions.region[0].reported} 单;当前 ${t?.overdue} 单在办故障已逾期。`:'所选条件下暂无本期故障,可调整日期或筛选范围。'}</strong><span>按数量描述,不代表故障发生率。</span><button disabled={!t?.open} onClick={showAllOpen}>查看关联故障 <ArrowUpRight size={13}/></button></div>
|
||||||
{tab==='多维分析'&&<div className="fd-analysis-banner"><div><h2>组合维度,追溯故障</h2><p>支持运维处置人、车牌、工单状态、车型、区域、客户等 12 个维度,多选筛选与明细下钻。</p></div><button className="fd-query" onClick={()=>drill('本期故障多维分析')}>打开多维分析</button></div>}
|
{tab==='多维分析'&&<div className="fd-analysis-banner"><div><h2>组合维度,追溯故障</h2><p>支持运维处置人、车牌、故障程度、工单状态、车型、区域、客户等 13 个维度,多选筛选与明细下钻。</p></div><button className="fd-query" onClick={()=>drill('本期故障多维分析')}>打开多维分析</button></div>}
|
||||||
<DashboardPanels data={data} drill={drill}/>
|
<DashboardPanels data={data} drill={drill}/>
|
||||||
<section className="fd-panel fd-focus" aria-label="重点故障跟进">
|
<section className="fd-panel fd-focus" aria-label="重点故障跟进">
|
||||||
<div className="fd-panel-title">
|
<div className="fd-panel-title">
|
||||||
|
|||||||
@@ -2,10 +2,10 @@ import type { AssetAnalysisDimension } from '../../../shared/assets/analytics';
|
|||||||
import type { FaultAnalysisRecord } from '../../../shared/assets/faults';
|
import type { FaultAnalysisRecord } from '../../../shared/assets/faults';
|
||||||
import { DIMENSION_LABELS, getAvailableDimensions } from '../analytics-model';
|
import { DIMENSION_LABELS, getAvailableDimensions } from '../analytics-model';
|
||||||
|
|
||||||
export type FaultAnalysisDimension = AssetAnalysisDimension | 'handler' | 'plate' | 'faultStatus';
|
export type FaultAnalysisDimension = AssetAnalysisDimension | 'handler' | 'plate' | 'faultStatus' | 'level';
|
||||||
export type FaultAnalysisFilters = Partial<Record<FaultAnalysisDimension, string[]>>;
|
export type FaultAnalysisFilters = Partial<Record<FaultAnalysisDimension, string[]>>;
|
||||||
export const FAULT_ANALYSIS_DIMENSIONS: FaultAnalysisDimension[] = getAvailableDimensions('assets').flatMap(d => d === 'type' ? [d, 'handler', 'plate', 'faultStatus'] : [d]);
|
export const FAULT_ANALYSIS_DIMENSIONS: FaultAnalysisDimension[] = getAvailableDimensions('assets').flatMap(d => d === 'type' ? [d, 'handler', 'plate', 'faultStatus', 'level'] : [d]);
|
||||||
export const FAULT_ANALYSIS_LABELS = { ...DIMENSION_LABELS, handler: '运维处置人', plate: '车牌', faultStatus: '故障工单状态' };
|
export const FAULT_ANALYSIS_LABELS = { ...DIMENSION_LABELS, handler: '运维处置人', plate: '车牌', faultStatus: '故障工单状态', level: '故障程度' };
|
||||||
export const FAULT_CLOSURE_LABELS: Record<string, string> = { closed: '已闭环', unclosed: '未闭环' };
|
export const FAULT_CLOSURE_LABELS: Record<string, string> = { closed: '已闭环', unclosed: '未闭环' };
|
||||||
export interface FaultAnalysisGroup {
|
export interface FaultAnalysisGroup {
|
||||||
key: string; values: Partial<Record<FaultAnalysisDimension, string>>;
|
key: string; values: Partial<Record<FaultAnalysisDimension, string>>;
|
||||||
@@ -14,6 +14,7 @@ export interface FaultAnalysisGroup {
|
|||||||
export function faultDimensionValues(row: FaultAnalysisRecord, dimension: FaultAnalysisDimension): string[] {
|
export function faultDimensionValues(row: FaultAnalysisRecord, dimension: FaultAnalysisDimension): string[] {
|
||||||
if (dimension === 'plate') return [row.plate?.trim() || '未关联车辆'];
|
if (dimension === 'plate') return [row.plate?.trim() || '未关联车辆'];
|
||||||
if (dimension === 'faultStatus') return [row.status === '已归档' ? 'closed' : 'unclosed'];
|
if (dimension === 'faultStatus') return [row.status === '已归档' ? 'closed' : 'unclosed'];
|
||||||
|
if (dimension === 'level') return [row.level?.trim() || '未标注'];
|
||||||
return dimension === 'handler' ? [...new Set(row.handlers.length ? row.handlers.map(h => h.value) : ['unassigned'])] : [row.cell[dimension]];
|
return dimension === 'handler' ? [...new Set(row.handlers.length ? row.handlers.map(h => h.value) : ['unassigned'])] : [row.cell[dimension]];
|
||||||
}
|
}
|
||||||
export function filterFaultRecords(rows: FaultAnalysisRecord[], filters: FaultAnalysisFilters = {}): FaultAnalysisRecord[] {
|
export function filterFaultRecords(rows: FaultAnalysisRecord[], filters: FaultAnalysisFilters = {}): FaultAnalysisRecord[] {
|
||||||
|
|||||||
Reference in New Issue
Block a user