2026-03-20 09:33:27 +08:00
|
|
|
using Laservall.Solidworks.Extension;
|
|
|
|
|
using Laservall.Solidworks.Model;
|
|
|
|
|
using Laservall.Solidworks.Windows.ViewModel;
|
|
|
|
|
using SolidWorks.Interop.sldworks;
|
2025-10-24 10:30:51 +08:00
|
|
|
using System;
|
|
|
|
|
using System.Collections.Generic;
|
2026-03-20 09:33:27 +08:00
|
|
|
using System.Collections.ObjectModel;
|
|
|
|
|
using System.Globalization;
|
|
|
|
|
using System.IO;
|
2025-10-24 10:30:51 +08:00
|
|
|
using System.Linq;
|
|
|
|
|
using System.Text;
|
|
|
|
|
using System.Windows;
|
|
|
|
|
using System.Windows.Controls;
|
|
|
|
|
using System.Windows.Data;
|
|
|
|
|
|
|
|
|
|
namespace Laservall.Solidworks.Windows
|
|
|
|
|
{
|
2026-03-20 09:33:27 +08:00
|
|
|
public class LevelToMarginConverter : IValueConverter
|
2025-10-24 10:30:51 +08:00
|
|
|
{
|
2026-03-20 09:33:27 +08:00
|
|
|
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
|
|
|
|
|
{
|
|
|
|
|
if (value is int level)
|
|
|
|
|
{
|
|
|
|
|
return new Thickness(level * 20, 0, 0, 0);
|
|
|
|
|
}
|
|
|
|
|
return new Thickness(0);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
|
|
|
|
|
{
|
|
|
|
|
throw new NotImplementedException();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public class ExpandIconConverter : IValueConverter
|
|
|
|
|
{
|
|
|
|
|
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
|
|
|
|
|
{
|
|
|
|
|
if (value is bool expanded)
|
|
|
|
|
{
|
|
|
|
|
return expanded ? "▼" : "▶";
|
|
|
|
|
}
|
|
|
|
|
return "▶";
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
|
|
|
|
|
{
|
|
|
|
|
throw new NotImplementedException();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public partial class ItemsPropWindow : System.Windows.Window
|
|
|
|
|
{
|
|
|
|
|
private List<BomItemModel> allBomItems = new List<BomItemModel>();
|
|
|
|
|
private HashSet<string> dirtyDocPaths = new HashSet<string>();
|
|
|
|
|
private BomSettings currentSettings;
|
|
|
|
|
|
|
|
|
|
private static readonly HashSet<string> StaticColumnProps = new HashSet<string>
|
|
|
|
|
{
|
|
|
|
|
"零件名称", "材质", "属性", "材料"
|
|
|
|
|
};
|
|
|
|
|
private static readonly Dictionary<string, string> PropNameToModelField = new Dictionary<string, string>
|
|
|
|
|
{
|
|
|
|
|
{ "PartName", "零件名称" },
|
|
|
|
|
{ "MaterialProp", "材料" },
|
|
|
|
|
{ "Material", "材质" },
|
|
|
|
|
{ "Classification", "属性" }
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
private static readonly Dictionary<string, string> BuiltInColumnNameMap = new Dictionary<string, string>
|
|
|
|
|
{
|
|
|
|
|
{ "层级", "LevelColumn" },
|
|
|
|
|
{ "图号", "DrawingNoColumn" },
|
|
|
|
|
{ "零件名称", "ConfigNameColumn" },
|
|
|
|
|
{ "属性", "ClassificationColumn" },
|
|
|
|
|
{ "材料", "MaterialPropColumn" },
|
|
|
|
|
{ "材质", "MaterialColumn" },
|
|
|
|
|
{ "数量", "QuantityColumn" },
|
|
|
|
|
{ "装配体", "IsAssemblyColumn" },
|
|
|
|
|
{ "外购件", "IsOutSourcingColumn" }
|
|
|
|
|
};
|
|
|
|
|
|
2025-10-24 10:30:51 +08:00
|
|
|
public ItemsPropWindow()
|
|
|
|
|
{
|
|
|
|
|
InitializeComponent();
|
2026-03-20 09:33:27 +08:00
|
|
|
if (DataContext == null)
|
|
|
|
|
{
|
|
|
|
|
DataContext = new ItemsViewModel();
|
|
|
|
|
}
|
|
|
|
|
currentSettings = BomSettings.Load();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private async void Window_Loaded(object sender, RoutedEventArgs e)
|
|
|
|
|
{
|
|
|
|
|
if (!(DataContext is ItemsViewModel viewModel))
|
|
|
|
|
{
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
viewModel.IsLoading = true;
|
|
|
|
|
viewModel.LoadingProgress = 0;
|
|
|
|
|
viewModel.LoadingProgressMaximum = 1;
|
|
|
|
|
viewModel.LoadingText = "正在读取数据...";
|
|
|
|
|
|
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
await System.Threading.Tasks.Task.Yield();
|
|
|
|
|
|
|
|
|
|
ModelDoc2 model = SWUtils.SwApp.ActiveDoc as ModelDoc2;
|
|
|
|
|
if (model == null)
|
|
|
|
|
{
|
|
|
|
|
viewModel.BomItems = new ObservableCollection<BomItemModel>();
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Configuration config = model.ConfigurationManager.ActiveConfiguration;
|
|
|
|
|
|
|
|
|
|
var allParts = new SwAssDocTreeModel()
|
|
|
|
|
{
|
|
|
|
|
drawingNo = Path.GetFileNameWithoutExtension(model.GetPathName()),
|
|
|
|
|
docPath = model.GetPathName(),
|
|
|
|
|
configName = config?.Name ?? "",
|
|
|
|
|
children = new List<SwAssDocTreeModel>(),
|
|
|
|
|
isAss = true,
|
|
|
|
|
quantity = 1,
|
|
|
|
|
props = SWUtils.GetConfig3(model.GetPathName())
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
Component2 rootComponent = config?.GetRootComponent3(true) as Component2;
|
|
|
|
|
int totalCount = GetTotalComponentCount(model);
|
|
|
|
|
|
|
|
|
|
viewModel.LoadingProgressMaximum = totalCount;
|
|
|
|
|
viewModel.LoadingText = "正在解析装配结构...";
|
|
|
|
|
|
|
|
|
|
SWUtils.SetAllPartResolved(model.GetPathName());
|
|
|
|
|
|
|
|
|
|
if (rootComponent != null)
|
|
|
|
|
{
|
|
|
|
|
var progress = new Progress<SwTreeLoadProgress>(p =>
|
|
|
|
|
{
|
|
|
|
|
viewModel.LoadingProgressMaximum = p.Total;
|
|
|
|
|
viewModel.LoadingProgress = p.Processed;
|
|
|
|
|
viewModel.LoadingText = string.IsNullOrWhiteSpace(p.CurrentName)
|
|
|
|
|
? $"正在读取数据 ({p.Processed}/{p.Total})"
|
|
|
|
|
: $"正在读取 {p.CurrentName} ({p.Processed}/{p.Total})";
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
var parts = await SWUtils.GetSwAssDocTreeChildrenAsync(rootComponent, true, true, totalCount, progress);
|
|
|
|
|
allParts.children.AddRange(parts);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
viewModel.LoadingText = "正在生成BOM...";
|
|
|
|
|
|
|
|
|
|
allBomItems = SWUtils.FlattenBomTree(allParts);
|
|
|
|
|
var propKeys = SWUtils.CollectAllPropertyKeys(allBomItems);
|
|
|
|
|
|
|
|
|
|
var dynamicKeys = propKeys
|
|
|
|
|
.Where(k => !StaticColumnProps.Contains(k))
|
|
|
|
|
.OrderBy(k => k)
|
|
|
|
|
.ToList();
|
|
|
|
|
|
|
|
|
|
currentSettings.MergeDynamicKeys(dynamicKeys);
|
|
|
|
|
|
|
|
|
|
var userAddedKeys = currentSettings.Columns
|
|
|
|
|
.Where(c => c.IsUserAdded && !dynamicKeys.Contains(c.Name))
|
|
|
|
|
.Select(c => c.Name)
|
|
|
|
|
.ToList();
|
|
|
|
|
dynamicKeys.AddRange(userAddedKeys);
|
|
|
|
|
|
|
|
|
|
EnsurePropsKeys(dynamicKeys);
|
|
|
|
|
|
|
|
|
|
viewModel.AllPropertyKeys = dynamicKeys;
|
|
|
|
|
AddDynamicColumns(dynamicKeys);
|
|
|
|
|
ApplyColumnVisibility();
|
|
|
|
|
|
|
|
|
|
ApplyViewMode(viewModel);
|
|
|
|
|
}
|
|
|
|
|
catch (Exception ex)
|
|
|
|
|
{
|
|
|
|
|
System.Windows.MessageBox.Show(this, ex.Message, "读取数据失败", MessageBoxButton.OK, MessageBoxImage.Error);
|
|
|
|
|
}
|
|
|
|
|
finally
|
|
|
|
|
{
|
|
|
|
|
viewModel.IsLoading = false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void AddDynamicColumns(List<string> propKeys)
|
|
|
|
|
{
|
|
|
|
|
foreach (var key in propKeys)
|
|
|
|
|
{
|
|
|
|
|
var column = new DataGridTextColumn
|
|
|
|
|
{
|
|
|
|
|
Header = key,
|
|
|
|
|
Width = new DataGridLength(100),
|
|
|
|
|
Binding = new Binding($"Props[{key}]")
|
|
|
|
|
{
|
|
|
|
|
Mode = BindingMode.TwoWay,
|
|
|
|
|
UpdateSourceTrigger = UpdateSourceTrigger.LostFocus
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
BomDataGrid.Columns.Add(column);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void EnsurePropsKeys(List<string> keys)
|
|
|
|
|
{
|
|
|
|
|
foreach (var item in allBomItems)
|
|
|
|
|
{
|
|
|
|
|
if (item.Props == null)
|
|
|
|
|
{
|
|
|
|
|
item.Props = new Dictionary<string, string>();
|
|
|
|
|
}
|
|
|
|
|
foreach (var key in keys)
|
|
|
|
|
{
|
|
|
|
|
if (!item.Props.ContainsKey(key))
|
|
|
|
|
{
|
|
|
|
|
item.Props[key] = "";
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void ApplyColumnVisibility()
|
|
|
|
|
{
|
|
|
|
|
if (currentSettings == null)
|
|
|
|
|
{
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
foreach (var col in BomDataGrid.Columns)
|
|
|
|
|
{
|
|
|
|
|
string header = col.Header?.ToString();
|
|
|
|
|
if (string.IsNullOrEmpty(header))
|
|
|
|
|
{
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var config = currentSettings.GetColumn(header);
|
|
|
|
|
if (config != null)
|
|
|
|
|
{
|
|
|
|
|
col.Visibility = config.IsVisible ? Visibility.Visible : Visibility.Collapsed;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void ColumnConfig_Click(object sender, RoutedEventArgs e)
|
|
|
|
|
{
|
|
|
|
|
var configWindow = new ColumnConfigWindow(currentSettings);
|
|
|
|
|
configWindow.Owner = this;
|
|
|
|
|
|
|
|
|
|
if (configWindow.ShowDialog() == true && configWindow.ResultSettings != null)
|
|
|
|
|
{
|
|
|
|
|
currentSettings = configWindow.ResultSettings;
|
|
|
|
|
currentSettings.Save();
|
|
|
|
|
|
|
|
|
|
if (DataContext is ItemsViewModel viewModel)
|
|
|
|
|
{
|
|
|
|
|
var existingDynamicHeaders = new HashSet<string>();
|
|
|
|
|
var toRemove = new List<DataGridColumn>();
|
|
|
|
|
foreach (var col in BomDataGrid.Columns)
|
|
|
|
|
{
|
|
|
|
|
string header = col.Header?.ToString();
|
|
|
|
|
if (!string.IsNullOrEmpty(header) && !BuiltInColumnNameMap.ContainsKey(header))
|
|
|
|
|
{
|
|
|
|
|
existingDynamicHeaders.Add(header);
|
|
|
|
|
toRemove.Add(col);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
foreach (var col in toRemove)
|
|
|
|
|
{
|
|
|
|
|
BomDataGrid.Columns.Remove(col);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var dynamicKeys = currentSettings.Columns
|
|
|
|
|
.Where(c => !c.IsFixed)
|
|
|
|
|
.Select(c => c.Name)
|
|
|
|
|
.ToList();
|
|
|
|
|
|
|
|
|
|
EnsurePropsKeys(dynamicKeys);
|
|
|
|
|
viewModel.AllPropertyKeys = dynamicKeys;
|
|
|
|
|
AddDynamicColumns(dynamicKeys);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ApplyColumnVisibility();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void ToggleExpand_Click(object sender, RoutedEventArgs e)
|
|
|
|
|
{
|
|
|
|
|
if (!(sender is System.Windows.Controls.Primitives.ToggleButton toggle))
|
|
|
|
|
{
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!(toggle.DataContext is BomItemModel item))
|
|
|
|
|
{
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
UpdateChildrenVisibility(item);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void UpdateChildrenVisibility(BomItemModel parentItem)
|
|
|
|
|
{
|
|
|
|
|
bool shouldShow = parentItem.IsExpanded;
|
|
|
|
|
var descendants = GetDescendants(parentItem.NodeId);
|
|
|
|
|
|
|
|
|
|
foreach (var child in descendants)
|
|
|
|
|
{
|
|
|
|
|
if (!shouldShow)
|
|
|
|
|
{
|
|
|
|
|
child.IsVisible = false;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
child.IsVisible = IsAncestorChainExpanded(child);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private bool IsAncestorChainExpanded(BomItemModel item)
|
|
|
|
|
{
|
|
|
|
|
int parentId = item.ParentNodeId;
|
|
|
|
|
while (parentId >= 0)
|
|
|
|
|
{
|
|
|
|
|
var parent = allBomItems.FirstOrDefault(b => b.NodeId == parentId);
|
|
|
|
|
if (parent == null)
|
|
|
|
|
{
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
if (!parent.IsExpanded)
|
|
|
|
|
{
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
parentId = parent.ParentNodeId;
|
|
|
|
|
}
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private List<BomItemModel> GetDescendants(int parentNodeId)
|
|
|
|
|
{
|
|
|
|
|
var result = new List<BomItemModel>();
|
|
|
|
|
var directChildren = allBomItems.Where(b => b.ParentNodeId == parentNodeId).ToList();
|
|
|
|
|
foreach (var child in directChildren)
|
|
|
|
|
{
|
|
|
|
|
result.Add(child);
|
|
|
|
|
result.AddRange(GetDescendants(child.NodeId));
|
|
|
|
|
}
|
|
|
|
|
return result;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void ExpandAll_Click(object sender, RoutedEventArgs e)
|
|
|
|
|
{
|
|
|
|
|
foreach (var item in allBomItems)
|
|
|
|
|
{
|
|
|
|
|
item.IsExpanded = true;
|
|
|
|
|
item.IsVisible = true;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void CollapseAll_Click(object sender, RoutedEventArgs e)
|
|
|
|
|
{
|
|
|
|
|
foreach (var item in allBomItems)
|
|
|
|
|
{
|
|
|
|
|
if (item.HasChildren)
|
|
|
|
|
{
|
|
|
|
|
item.IsExpanded = false;
|
|
|
|
|
}
|
|
|
|
|
item.IsVisible = item.Level == 0;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void BomDataGrid_CellEditEnding(object sender, DataGridCellEditEndingEventArgs e)
|
|
|
|
|
{
|
|
|
|
|
if (e.EditAction == DataGridEditAction.Cancel)
|
|
|
|
|
{
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!(e.Row.Item is BomItemModel item))
|
|
|
|
|
{
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!string.IsNullOrEmpty(item.DocPath))
|
|
|
|
|
{
|
|
|
|
|
dirtyDocPaths.Add(item.DocPath);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (e.EditingElement is TextBox textBox)
|
|
|
|
|
{
|
|
|
|
|
string newValue = textBox.Text;
|
|
|
|
|
string header = e.Column.Header?.ToString() ?? "";
|
|
|
|
|
|
|
|
|
|
if (PropNameToModelField.ContainsValue(header))
|
|
|
|
|
{
|
|
|
|
|
if (item.Props != null)
|
|
|
|
|
{
|
|
|
|
|
item.Props[header] = newValue;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else if (item.Props != null && item.Props.ContainsKey(header))
|
|
|
|
|
{
|
|
|
|
|
item.Props[header] = newValue;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void SaveChanges_Click(object sender, RoutedEventArgs e)
|
|
|
|
|
{
|
|
|
|
|
if (dirtyDocPaths.Count == 0)
|
|
|
|
|
{
|
|
|
|
|
System.Windows.MessageBox.Show(this, "没有需要保存的修改", "提示", MessageBoxButton.OK, MessageBoxImage.Information);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
int savedCount = 0;
|
|
|
|
|
foreach (var docPath in dirtyDocPaths)
|
|
|
|
|
{
|
|
|
|
|
var item = allBomItems.FirstOrDefault(b => b.DocPath == docPath);
|
|
|
|
|
if (item == null || item.Props == null)
|
|
|
|
|
{
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ModelDoc2 doc = SWUtils.OpenDocSilently(docPath);
|
|
|
|
|
if (doc == null)
|
|
|
|
|
{
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Configuration activeConfig = doc.ConfigurationManager?.ActiveConfiguration;
|
|
|
|
|
CustomPropertyManager configPropMgr = activeConfig?.CustomPropertyManager;
|
|
|
|
|
|
|
|
|
|
CustomPropertyManager filePropMgr = null;
|
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
filePropMgr = doc.Extension?.get_CustomPropertyManager("");
|
|
|
|
|
}
|
|
|
|
|
catch { }
|
|
|
|
|
|
|
|
|
|
foreach (var kvp in item.Props)
|
|
|
|
|
{
|
|
|
|
|
if (string.IsNullOrEmpty(kvp.Key)) continue;
|
|
|
|
|
|
|
|
|
|
bool writtenToConfig = false;
|
|
|
|
|
if (configPropMgr != null)
|
|
|
|
|
{
|
|
|
|
|
var configNames = configPropMgr.GetNames() as string[];
|
|
|
|
|
if (configNames != null && configNames.Contains(kvp.Key))
|
|
|
|
|
{
|
|
|
|
|
configPropMgr.Set2(kvp.Key, kvp.Value ?? "");
|
|
|
|
|
writtenToConfig = true;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!writtenToConfig && filePropMgr != null)
|
|
|
|
|
{
|
|
|
|
|
filePropMgr.Set2(kvp.Key, kvp.Value ?? "");
|
|
|
|
|
}
|
|
|
|
|
else if (!writtenToConfig && configPropMgr != null)
|
|
|
|
|
{
|
|
|
|
|
configPropMgr.Set2(kvp.Key, kvp.Value ?? "");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
doc.Save3(0, 0, 0);
|
|
|
|
|
savedCount++;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
dirtyDocPaths.Clear();
|
|
|
|
|
System.Windows.MessageBox.Show(this, $"已保存 {savedCount} 个文件的属性修改", "保存成功", MessageBoxButton.OK, MessageBoxImage.Information);
|
|
|
|
|
}
|
|
|
|
|
catch (Exception ex)
|
|
|
|
|
{
|
|
|
|
|
System.Windows.MessageBox.Show(this, ex.Message, "保存失败", MessageBoxButton.OK, MessageBoxImage.Error);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void SearchBox_TextChanged(object sender, TextChangedEventArgs e)
|
|
|
|
|
{
|
|
|
|
|
if (!(DataContext is ItemsViewModel viewModel))
|
|
|
|
|
{
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
string keyword = SearchBox.Text?.Trim() ?? "";
|
|
|
|
|
var sourceList = GetCurrentSourceList();
|
|
|
|
|
|
|
|
|
|
if (string.IsNullOrEmpty(keyword))
|
|
|
|
|
{
|
|
|
|
|
viewModel.BomItems = new ObservableCollection<BomItemModel>(sourceList);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var filtered = sourceList.Where(item =>
|
|
|
|
|
(item.DrawingNo != null && item.DrawingNo.IndexOf(keyword, StringComparison.OrdinalIgnoreCase) >= 0) ||
|
|
|
|
|
(item.ConfigName != null && item.ConfigName.IndexOf(keyword, StringComparison.OrdinalIgnoreCase) >= 0) ||
|
|
|
|
|
(item.Material != null && item.Material.IndexOf(keyword, StringComparison.OrdinalIgnoreCase) >= 0) ||
|
|
|
|
|
(item.MaterialProp != null && item.MaterialProp.IndexOf(keyword, StringComparison.OrdinalIgnoreCase) >= 0) ||
|
|
|
|
|
(item.Classification != null && item.Classification.IndexOf(keyword, StringComparison.OrdinalIgnoreCase) >= 0) ||
|
|
|
|
|
(item.Props != null && item.Props.Values.Any(v => v != null && v.IndexOf(keyword, StringComparison.OrdinalIgnoreCase) >= 0))
|
|
|
|
|
).ToList();
|
|
|
|
|
|
|
|
|
|
viewModel.BomItems = new ObservableCollection<BomItemModel>(filtered);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void ExportExcel_Click(object sender, RoutedEventArgs e)
|
|
|
|
|
{
|
|
|
|
|
if (!(DataContext is ItemsViewModel viewModel) || viewModel.BomItems == null || viewModel.BomItems.Count == 0)
|
|
|
|
|
{
|
|
|
|
|
System.Windows.MessageBox.Show(this, "没有数据可导出", "提示", MessageBoxButton.OK, MessageBoxImage.Information);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var dialog = new Microsoft.Win32.SaveFileDialog
|
|
|
|
|
{
|
|
|
|
|
Filter = "CSV 文件|*.csv",
|
|
|
|
|
DefaultExt = ".csv",
|
|
|
|
|
FileName = "BOM_Export"
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
if (dialog.ShowDialog() != true)
|
|
|
|
|
{
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
var sb = new StringBuilder();
|
|
|
|
|
bool isFlatView = viewModel.IsFlatView;
|
|
|
|
|
|
|
|
|
|
var builtInDefs = new List<(string Name, Func<BomItemModel, string> Getter, bool FlatOnly)>
|
|
|
|
|
{
|
|
|
|
|
("层级", item => item.LevelDisplay ?? "", false),
|
|
|
|
|
("图号", item => item.DrawingNo ?? "", false),
|
|
|
|
|
("零件名称", item => item.ConfigName ?? "", false),
|
|
|
|
|
("属性", item => item.Classification ?? "", false),
|
|
|
|
|
("材料", item => item.MaterialProp ?? "", false),
|
|
|
|
|
("材质", item => item.Material ?? "", false),
|
|
|
|
|
("数量", item => item.Quantity.ToString(), false),
|
|
|
|
|
("装配体", item => item.IsAssembly ? "是" : "否", false),
|
|
|
|
|
("外购件", item => item.IsOutSourcing ? "是" : "否", false)
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
var exportBuiltIns = builtInDefs
|
|
|
|
|
.Where(d => currentSettings.IsColumnExport(d.Name))
|
|
|
|
|
.Where(d => !isFlatView || (d.Name != "层级" && d.Name != "装配体"))
|
|
|
|
|
.ToList();
|
|
|
|
|
|
|
|
|
|
var exportDynamicKeys = (viewModel.AllPropertyKeys ?? new List<string>())
|
|
|
|
|
.Where(k => currentSettings.IsColumnExport(k))
|
|
|
|
|
.ToList();
|
|
|
|
|
|
|
|
|
|
var headers = exportBuiltIns.Select(d => d.Name).Concat(exportDynamicKeys).ToList();
|
|
|
|
|
sb.AppendLine(string.Join(",", headers.Select(EscapeCsvField)));
|
|
|
|
|
|
|
|
|
|
foreach (var item in viewModel.BomItems)
|
|
|
|
|
{
|
|
|
|
|
var fields = new List<string>();
|
|
|
|
|
foreach (var def in exportBuiltIns)
|
|
|
|
|
{
|
|
|
|
|
fields.Add(def.Getter(item));
|
|
|
|
|
}
|
|
|
|
|
foreach (var key in exportDynamicKeys)
|
|
|
|
|
{
|
|
|
|
|
fields.Add(item.GetProp(key));
|
|
|
|
|
}
|
|
|
|
|
sb.AppendLine(string.Join(",", fields.Select(EscapeCsvField)));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
File.WriteAllText(dialog.FileName, sb.ToString(), Encoding.UTF8);
|
|
|
|
|
System.Windows.MessageBox.Show(this, $"已导出到:\n{dialog.FileName}", "导出成功", MessageBoxButton.OK, MessageBoxImage.Information);
|
|
|
|
|
}
|
|
|
|
|
catch (Exception ex)
|
|
|
|
|
{
|
|
|
|
|
System.Windows.MessageBox.Show(this, ex.Message, "导出失败", MessageBoxButton.OK, MessageBoxImage.Error);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private static string EscapeCsvField(string field)
|
|
|
|
|
{
|
|
|
|
|
if (string.IsNullOrEmpty(field))
|
|
|
|
|
{
|
|
|
|
|
return "";
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (field.Contains(",") || field.Contains("\"") || field.Contains("\n"))
|
|
|
|
|
{
|
|
|
|
|
return "\"" + field.Replace("\"", "\"\"") + "\"";
|
|
|
|
|
}
|
|
|
|
|
return field;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void FlatViewToggle_Changed(object sender, RoutedEventArgs e)
|
|
|
|
|
{
|
|
|
|
|
if (!(DataContext is ItemsViewModel viewModel))
|
|
|
|
|
{
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ApplyViewMode(viewModel);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void ApplyViewMode(ItemsViewModel viewModel)
|
|
|
|
|
{
|
|
|
|
|
bool isFlatView = viewModel.IsFlatView;
|
|
|
|
|
|
|
|
|
|
LevelColumn.Visibility = isFlatView ? Visibility.Collapsed : Visibility.Visible;
|
|
|
|
|
ExpandAllBtn.Visibility = isFlatView ? Visibility.Collapsed : Visibility.Visible;
|
|
|
|
|
CollapseAllBtn.Visibility = isFlatView ? Visibility.Collapsed : Visibility.Visible;
|
|
|
|
|
|
|
|
|
|
if (isFlatView)
|
|
|
|
|
{
|
|
|
|
|
var flat = allBomItems
|
|
|
|
|
.Where(i => !i.IsAssembly)
|
|
|
|
|
.GroupBy(i => i.DrawingNo ?? "")
|
|
|
|
|
.Select(g =>
|
|
|
|
|
{
|
|
|
|
|
var first = g.First();
|
|
|
|
|
return new BomItemModel
|
|
|
|
|
{
|
|
|
|
|
Level = 0,
|
|
|
|
|
LevelDisplay = "",
|
|
|
|
|
DrawingNo = first.DrawingNo,
|
|
|
|
|
ConfigName = first.ConfigName,
|
|
|
|
|
PartName = first.PartName,
|
|
|
|
|
MaterialProp = first.MaterialProp,
|
|
|
|
|
Material = first.Material,
|
|
|
|
|
Classification = first.Classification,
|
|
|
|
|
Quantity = g.Sum(x => x.Quantity),
|
|
|
|
|
IsAssembly = false,
|
|
|
|
|
IsOutSourcing = first.IsOutSourcing,
|
|
|
|
|
DocPath = first.DocPath,
|
|
|
|
|
Props = first.Props,
|
|
|
|
|
HasChildren = false,
|
|
|
|
|
IsExpanded = false,
|
|
|
|
|
IsVisible = true
|
|
|
|
|
};
|
|
|
|
|
})
|
|
|
|
|
.ToList();
|
|
|
|
|
|
|
|
|
|
viewModel.BomItems = new ObservableCollection<BomItemModel>(flat);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
viewModel.BomItems = new ObservableCollection<BomItemModel>(allBomItems);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private List<BomItemModel> GetCurrentSourceList()
|
|
|
|
|
{
|
|
|
|
|
if (!(DataContext is ItemsViewModel viewModel))
|
|
|
|
|
{
|
|
|
|
|
return allBomItems;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (viewModel.IsFlatView)
|
|
|
|
|
{
|
|
|
|
|
return allBomItems
|
|
|
|
|
.Where(i => !i.IsAssembly)
|
|
|
|
|
.GroupBy(i => i.DrawingNo ?? "")
|
|
|
|
|
.Select(g =>
|
|
|
|
|
{
|
|
|
|
|
var first = g.First();
|
|
|
|
|
return new BomItemModel
|
|
|
|
|
{
|
|
|
|
|
Level = 0,
|
|
|
|
|
LevelDisplay = "",
|
|
|
|
|
DrawingNo = first.DrawingNo,
|
|
|
|
|
ConfigName = first.ConfigName,
|
|
|
|
|
PartName = first.PartName,
|
|
|
|
|
MaterialProp = first.MaterialProp,
|
|
|
|
|
Material = first.Material,
|
|
|
|
|
Classification = first.Classification,
|
|
|
|
|
Quantity = g.Sum(x => x.Quantity),
|
|
|
|
|
IsAssembly = false,
|
|
|
|
|
IsOutSourcing = first.IsOutSourcing,
|
|
|
|
|
DocPath = first.DocPath,
|
|
|
|
|
Props = first.Props,
|
|
|
|
|
HasChildren = false,
|
|
|
|
|
IsExpanded = false,
|
|
|
|
|
IsVisible = true
|
|
|
|
|
};
|
|
|
|
|
})
|
|
|
|
|
.ToList();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return allBomItems;
|
2025-10-24 10:30:51 +08:00
|
|
|
}
|
|
|
|
|
|
2026-03-20 09:33:27 +08:00
|
|
|
private int GetTotalComponentCount(ModelDoc2 model)
|
2025-10-24 10:30:51 +08:00
|
|
|
{
|
2026-03-20 09:33:27 +08:00
|
|
|
AssemblyDoc assembly = model as AssemblyDoc;
|
|
|
|
|
if (assembly == null)
|
|
|
|
|
{
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
2025-10-24 10:30:51 +08:00
|
|
|
|
2026-03-20 09:33:27 +08:00
|
|
|
object[] components = assembly.GetComponents(true) as object[];
|
|
|
|
|
return Math.Max((components?.Length ?? 0) + 1, 1);
|
2025-10-24 10:30:51 +08:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|