Laservall_solidworks_inject/bom-ui/src/components/ToolBar.tsx

206 lines
5.9 KiB
TypeScript
Raw Normal View History

import React, { useState } from 'react';
import { Space, Button, Segmented, Badge, message, Modal, Table, Tag } from 'antd';
import { apiPost, getExportUrl } from '../api/client';
import type { SaveChangeDto, BomItemDto, MatchMaterialRequest, MatchMaterialResponse, MatchMaterialResultDto } from '../api/types';
interface ToolBarProps {
dirtyCount: number;
viewMode: 'hierarchical' | 'flat';
onViewModeChange: (mode: 'hierarchical' | 'flat') => void;
onRefresh: () => void;
onOpenConfig: () => void;
getDirtyChanges: () => SaveChangeDto[];
onSaveSuccess: (changes: SaveChangeDto[]) => void;
items: BomItemDto[];
}
export const ToolBar: React.FC<ToolBarProps> = ({
dirtyCount,
viewMode,
onViewModeChange,
onRefresh,
onOpenConfig,
getDirtyChanges,
onSaveSuccess,
items,
}) => {
const [saving, setSaving] = useState(false);
const [matching, setMatching] = useState(false);
const [matchResults, setMatchResults] = useState<MatchMaterialResultDto[]>([]);
const [matchModalVisible, setMatchModalVisible] = useState(false);
const [matchSaving, setMatchSaving] = useState(false);
const handleSave = async () => {
if (dirtyCount === 0) return;
setSaving(true);
try {
const changes = getDirtyChanges();
const res = await apiPost<any>('/api/bom/save', { Changes: changes });
if (res.Success) {
message.success(`保存成功,共更新 ${res.SavedCount}`);
onSaveSuccess(changes);
} else {
message.error(`保存失败: ${res.Error}`);
}
} catch (e: any) {
message.error(`保存失败: ${e.message}`);
} finally {
setSaving(false);
}
};
const handleExport = () => {
window.open(getExportUrl(viewMode === 'flat'), '_blank');
};
const handleMatchMaterial = async () => {
const emptyItems = items.filter(
(item) => !item.IsAssembly && !item.Props?.['物料编码']
);
if (emptyItems.length === 0) {
message.info('所有零件的物料编码均已填写');
return;
}
setMatching(true);
try {
const request: MatchMaterialRequest = {
Items: emptyItems.map((item) => ({
DrawingNo: item.DrawingNo,
DocPath: item.DocPath,
})),
};
const res = await apiPost<MatchMaterialResponse>('/api/bom/match-material', request);
if (!res.Success) {
message.error(`匹配失败: ${res.Error}`);
return;
}
const matched = res.Results.filter((r) => r.Matched);
if (matched.length === 0) {
message.info('未找到匹配的物料编码');
return;
}
setMatchResults(matched);
setMatchModalVisible(true);
} catch (e: any) {
message.error(`匹配失败: ${e.message}`);
} finally {
setMatching(false);
}
};
const handleConfirmMatch = async () => {
setMatchSaving(true);
try {
const changes: SaveChangeDto[] = matchResults.map((r) => ({
DocPath: r.DocPath,
Key: '物料编码',
Value: r.PartCode,
}));
const res = await apiPost<any>('/api/bom/save', { Changes: changes });
if (res.Success) {
message.success(`写入成功,共更新 ${res.Results?.length ?? changes.length} 项物料编码`);
onSaveSuccess(changes);
setMatchModalVisible(false);
setMatchResults([]);
} else {
message.error(`写入失败: ${res.Error}`);
}
} catch (e: any) {
message.error(`写入失败: ${e.message}`);
} finally {
setMatchSaving(false);
}
};
const matchColumns = [
{
title: '图号',
dataIndex: 'DrawingNo',
key: 'DrawingNo',
width: 260,
},
{
title: '物料编码',
dataIndex: 'PartCode',
key: 'PartCode',
width: 180,
},
{
title: 'PLM零件名称',
dataIndex: 'PartName',
key: 'PartName',
width: 180,
},
{
title: '状态',
key: 'status',
width: 80,
render: (_: unknown, record: MatchMaterialResultDto) =>
record.Matched ? <Tag color="green"></Tag> : <Tag color="red"></Tag>,
},
];
return (
<>
<Space style={{ marginBottom: 16, display: 'flex', justifyContent: 'space-between', width: '100%' }}>
<Space>
<Badge count={dirtyCount}>
<Button type="primary" onClick={handleSave} disabled={dirtyCount === 0} loading={saving}>
</Button>
</Badge>
<Button onClick={handleExport}></Button>
<Button onClick={onRefresh}></Button>
<Button onClick={onOpenConfig}></Button>
<Button onClick={handleMatchMaterial} loading={matching}>
</Button>
</Space>
<Space>
<Segmented
options={[
{ label: '层级视图', value: 'hierarchical' },
{ label: '扁平视图', value: 'flat' },
]}
value={viewMode}
onChange={(val) => onViewModeChange(val as 'hierarchical' | 'flat')}
/>
<div style={{ marginLeft: 16, color: '#52c41a' }}> </div>
</Space>
</Space>
<Modal
title={`匹配物料编号 (${matchResults.length} 项匹配)`}
open={matchModalVisible}
onOk={handleConfirmMatch}
onCancel={() => {
setMatchModalVisible(false);
setMatchResults([]);
}}
okText="确认写入"
cancelText="取消"
confirmLoading={matchSaving}
width={760}
>
<p style={{ marginBottom: 12, color: '#666' }}>
SolidWorks文件属性
</p>
<Table
dataSource={matchResults}
columns={matchColumns}
rowKey="DocPath"
size="small"
pagination={false}
scroll={{ y: 400 }}
/>
</Modal>
</>
);
};