1231 lines
51 KiB
C#
1231 lines
51 KiB
C#
|
||
|
||
using System;
|
||
using System.ComponentModel.DataAnnotations;
|
||
using System.Diagnostics;
|
||
using System.Linq;
|
||
using System.Reflection;
|
||
using System.Text;
|
||
using System.Text.RegularExpressions;
|
||
using System.Threading.Tasks;
|
||
using System.Windows;
|
||
|
||
using Autodesk.Revit.DB;
|
||
using Autodesk.Revit.DB.Electrical;
|
||
using Autodesk.Revit.DB.Mechanical;
|
||
using Autodesk.Revit.DB.Plumbing;
|
||
using Autodesk.Revit.UI;
|
||
|
||
using Microsoft.Win32;
|
||
|
||
using Spire.Doc.Documents;
|
||
|
||
using Word = Spire.Doc;
|
||
|
||
namespace Szmedi.RvKits.ModelManager
|
||
{
|
||
public partial class ModelReviewViewModel : ObservableValidator
|
||
{
|
||
[ObservableProperty]
|
||
private bool isCivil = true;
|
||
|
||
[ObservableProperty]
|
||
private bool belongFloor = true;
|
||
|
||
[ObservableProperty]
|
||
private bool szComponent = true;
|
||
[ObservableProperty]
|
||
private bool listEveryErrors = true;
|
||
[ObservableProperty]
|
||
[StringLength(3, MinimumLength = 3)]
|
||
[NotifyCanExecuteChangedFor(nameof(ReviewModelCommand), nameof(ReviewModelsCommand))]
|
||
private string number = "001";
|
||
|
||
[ObservableProperty]
|
||
private string outputFolder = string.IsNullOrEmpty(Properties.Settings.Default.ReviewOutputFolder) ? Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.Desktop), $"审核报告{DateTime.Now:yyyy-MM-dd}") : Properties.Settings.Default.ReviewOutputFolder;
|
||
|
||
[ObservableProperty]
|
||
[StringLength(3, MinimumLength = 4)]
|
||
private string reviewCom = "深圳市市政设计研究院有限公司";
|
||
|
||
[ObservableProperty]
|
||
[NotifyCanExecuteChangedFor(nameof(ReviewModelCommand), nameof(ReviewModelsCommand))]
|
||
[StringLength(20, MinimumLength = 2)]
|
||
private string reviewer;
|
||
[ObservableProperty]
|
||
[StringLength(100, MinimumLength = 1)]
|
||
private string reviewerComments = "请按照详细意见进行修改";
|
||
|
||
private readonly string templateFile = Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location), "Templates", "ModelReviewTemplate.docx");
|
||
|
||
private readonly UIApplication uiapp;
|
||
private string disc;
|
||
public ModelReviewViewModel(UIApplication uiapp)
|
||
{
|
||
this.uiapp = uiapp;
|
||
Reviewer = uiapp.Application.Username;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 填写表单
|
||
/// </summary>
|
||
/// <param name="docx"></param>
|
||
/// <param name="reviewItems"></param>
|
||
private void FillReviewItems(Word.Document docx, List<ReviewItem> reviewItems)
|
||
{
|
||
var section = docx.Sections[0];
|
||
var table = section.Tables[1];
|
||
//复制表行,扣除原有行
|
||
for (var i = 0; i < reviewItems.Count - 1; i++)
|
||
{
|
||
var row = table.Rows[2].Clone();
|
||
table.Rows.Insert(3, row);
|
||
}
|
||
var rowIndex = 2;
|
||
var detail = new StringBuilder();
|
||
var detailtitles = new List<string>();
|
||
// 遍历表格中的行
|
||
for (var i = 0; i < reviewItems.Count; i++)
|
||
{
|
||
var item = reviewItems[i];
|
||
// 获取当前行
|
||
var row = table.Rows[rowIndex];
|
||
//第一列
|
||
var p1 = row.Cells[0].FirstParagraph;
|
||
p1 ??= row.Cells[0].AddParagraph();
|
||
p1.Text = $"{i + 1}";
|
||
//第二列
|
||
var p2 = row.Cells[1].FirstParagraph;
|
||
p2 ??= row.Cells[1].AddParagraph();
|
||
p2.Text = item.ModelFileName;
|
||
//第三列
|
||
var p3 = row.Cells[2].FirstParagraph;
|
||
p3 ??= row.Cells[2].AddParagraph();
|
||
p3.Text = item.Comment;
|
||
|
||
rowIndex++;
|
||
detailtitles.Add(item.Comment);
|
||
|
||
detail.Append($"{item}").AppendLine();
|
||
|
||
//detail.Append(item.DetailComment.ToString());
|
||
}
|
||
docx.Replace("${DetailComments}", $"{detail}", true, true);
|
||
|
||
foreach (var title in detailtitles)
|
||
{
|
||
var selection = docx.FindAllString(title, false, true).Last();
|
||
if (selection != null)
|
||
{
|
||
var titleRange = selection.GetAsOneRange();
|
||
titleRange.CharacterFormat.Bold = true;
|
||
titleRange.CharacterFormat.TextColor = System.Drawing.Color.Red;
|
||
titleRange.CharacterFormat.FontSize = 14;
|
||
}
|
||
}
|
||
var infos = docx.FindAllString("{族名:类型名/实例名:元素Id}", false, true);
|
||
if (infos != null)
|
||
{
|
||
foreach (var info in infos)
|
||
{
|
||
var textrange = info.GetAsOneRange();
|
||
textrange.CharacterFormat.FontSize = 10;
|
||
}
|
||
}
|
||
}
|
||
private static void InsertTextWatermark(Word.Document docx)
|
||
{
|
||
var section = docx.Sections[0];
|
||
var txtWatermark = new Word.TextWatermark
|
||
{
|
||
Text = "SZMEDI",
|
||
FontSize = 100,
|
||
Color = System.Drawing.Color.Gray,
|
||
Layout = WatermarkLayout.Diagonal,
|
||
};
|
||
section.Document.Watermark = txtWatermark;
|
||
|
||
}
|
||
|
||
/// <summary>
|
||
/// 管线附件
|
||
/// </summary>
|
||
/// <returns></returns>
|
||
private List<ReviewItem> ReviewAccessoryConnection(Document doc)
|
||
{
|
||
var instances = doc.OfType<FamilyInstance>()
|
||
.Where(ins => ins.Category.ToBuiltInCategory() == BuiltInCategory.OST_MechanicalEquipment);
|
||
|
||
return [];
|
||
}
|
||
/// <summary>
|
||
/// 审核项目,通用检查项并保存
|
||
/// </summary>
|
||
/// <param name="doc"></param>
|
||
/// <param name="message"></param>
|
||
/// <returns></returns>
|
||
private async Task<bool> ReviewDocumentAndSaveDocx(Document doc, StringBuilder message)
|
||
{
|
||
var project = string.Empty;
|
||
var engineering = string.Empty;
|
||
var discipline = string.Empty;
|
||
var phase = string.Empty;
|
||
var modelCom = string.Empty;
|
||
var path = doc.PathName;
|
||
var rvtfileName = Path.GetFileNameWithoutExtension(path);
|
||
|
||
var array = rvtfileName.Split('-', '/', '_').Select(s => s.Trim()).ToArray();
|
||
|
||
if (array.Length < 3)
|
||
{
|
||
message.AppendLine($"文件:{rvtfileName};命名不符合拆分规则:线路-工程(站场等)-专业-工程阶段-其他");
|
||
}
|
||
|
||
Word.Document docx = new();
|
||
docx.LoadFromFile(templateFile);
|
||
//添加水印
|
||
InsertTextWatermark(docx);
|
||
var lineRegex = new Regex(@"^\d+号线[\u4e00-\u9fff]*");
|
||
var phaseRegex = new Regex(@"^(设计|施工)阶段$");
|
||
var engineerRegex = new Regex(@"^[\u4e00-\u9fff]*(车辆段|站|区间|停车场|主变电|变电所)$");
|
||
var disciplineRegex = new Regex(@"^(通风|建筑|结构|轨道|机电|土建|围护|给排水|([\u4e00-\u9fff]电)|综合|通信|信号|管线|装修|场布|场地)");
|
||
foreach (var item in array)
|
||
{
|
||
if (lineRegex.IsMatch(item) && string.IsNullOrEmpty(project))
|
||
{
|
||
project = item;
|
||
continue;
|
||
}
|
||
if (phaseRegex.IsMatch(item) && string.IsNullOrEmpty(phase))
|
||
{
|
||
phase = item;
|
||
continue;
|
||
}
|
||
if (engineerRegex.IsMatch(item) && string.IsNullOrEmpty(engineering))
|
||
{
|
||
engineering = item;
|
||
continue;
|
||
}
|
||
if (disciplineRegex.IsMatch(item) && string.IsNullOrEmpty(discipline))
|
||
{
|
||
discipline = item;
|
||
}
|
||
}
|
||
disc = discipline;
|
||
//从文件名未能获得阶段时
|
||
if (string.IsNullOrEmpty(phase))
|
||
{
|
||
var phaseParam = doc.ProjectInformation.GetParameters("建设说明-建设阶段").FirstOrDefault();
|
||
if (phaseParam != null && !string.IsNullOrEmpty(phaseParam.AsString()))
|
||
{
|
||
phase = phaseParam.AsString();
|
||
}
|
||
}
|
||
//从构件读取,创建单位
|
||
var elems = doc.OfParentModelCollector();
|
||
if (elems == null)
|
||
{
|
||
return false;
|
||
}
|
||
foreach (var elem in elems)
|
||
{
|
||
var param = elem.GetParameters("CM-100-创建单位").FirstOrDefault();
|
||
if (param != null && !string.IsNullOrEmpty(param.AsString()))
|
||
{
|
||
modelCom = param.AsString();
|
||
break;
|
||
}
|
||
}
|
||
|
||
docx.Replace("${Project}", project, true, true);
|
||
docx.Replace("${Engineering}", engineering, true, true);
|
||
docx.Replace("${Discipline}", discipline.Trim('专', '业', '模', '型'), true, true);
|
||
docx.Replace("${Phase}", phase, true, true);
|
||
docx.Replace("${ModelCom}", modelCom, true, true);
|
||
docx.Replace("${ReviewCom}", ReviewCom, true, true);
|
||
docx.Replace("${Time}", DateTime.Now.ToString("yyyy-MM-dd"), true, true);
|
||
docx.Replace("${Year}", DateTime.Now.ToString("yyyy"), true, true);
|
||
docx.Replace("${Number}", Number, true, true);
|
||
docx.Replace("${Reviewer}", Reviewer, true, true);
|
||
docx.Replace("${ReviewComments}", ReviewerComments, true, true);
|
||
|
||
var items = new List<ReviewItem>();
|
||
items.AddRange(ReviewLocation(doc));
|
||
items.AddRange(ReviewName(doc));
|
||
items.AddRange(ReviewProperties(doc));
|
||
if (!IsCivil)
|
||
{
|
||
items.AddRange(ReviewFacilityConnection(doc));
|
||
items.AddRange(ReviewMEPCurveConnection(doc));
|
||
//items.AddRange(ReviewInstanceColor(doc));
|
||
items.AddRange(ReviewMEPCurveSlope(doc));
|
||
}
|
||
|
||
//var li = items.SelectMany(i => i.ReviewElements);
|
||
//if (li.Count() > 1000 && ListEveryErrors)
|
||
//{
|
||
// var result = MessageBox.Show("所有不符合要求的元素条目合计超过1000条,数量过多,写入word可能时间较长,\r\n请考虑取消逐项列举,是否继续导出?", "提示", MessageBoxButton.YesNo, MessageBoxImage.Question);
|
||
// if (result == MessageBoxResult.No)
|
||
// {
|
||
// ListEveryErrors = false;
|
||
// items.ForEach(item => item.ListErrors = false);
|
||
// //foreach (var item in items)
|
||
// //{
|
||
|
||
// //}
|
||
// //return false;
|
||
// }
|
||
//}
|
||
//return false;
|
||
FillReviewItems(docx, items);
|
||
try
|
||
{
|
||
await Task.Run(
|
||
() =>
|
||
{
|
||
var docxFileName = Path.Combine($"{OutputFolder}", $"{rvtfileName}.docx");
|
||
docx.SaveToFile(docxFileName, Word.FileFormat.Docx);
|
||
});
|
||
return true;
|
||
}
|
||
catch (IOException ex)
|
||
{
|
||
MessageBox.Show(ex.Message);
|
||
return false;
|
||
}
|
||
}
|
||
private bool IsConnectToSystem(FamilyInstance ins)
|
||
{
|
||
bool hasConnectToSystem = false;
|
||
if (ins.MEPModel == null || ins.MEPModel.ConnectorManager == null)
|
||
{
|
||
return true;
|
||
}
|
||
if (ins.get_BoundingBox(null) != null &&
|
||
ins.MEPModel.ConnectorManager != null)
|
||
{
|
||
var cons = ins.GetConnectors(false)
|
||
.OfType<Connector>()
|
||
.Where(
|
||
con => con.ConnectorType == ConnectorType.End &&
|
||
con.Domain != Domain.DomainElectrical &&
|
||
con.ConnectorType == ConnectorType.Surface);
|
||
if (cons.Count() == 0)
|
||
{
|
||
return true;
|
||
}
|
||
foreach (Connector con in cons)
|
||
{
|
||
try
|
||
{
|
||
if (con.IsConnected)
|
||
{
|
||
hasConnectToSystem = true;
|
||
break;
|
||
}
|
||
}
|
||
catch (Exception)
|
||
{
|
||
//hasConnectToSystem = true;
|
||
}
|
||
}
|
||
}
|
||
return hasConnectToSystem;
|
||
|
||
}
|
||
/// <summary>
|
||
/// 设备连接
|
||
/// </summary>
|
||
/// <returns></returns>
|
||
private List<ReviewItem> ReviewFacilityConnection(Document doc)
|
||
{
|
||
var facilityReviews = new List<ReviewItem>();
|
||
var errorFacilities = doc.OfParentModelCollector().OfType<FamilyInstance>()
|
||
.Where(ins => !IsConnectToSystem(ins));
|
||
ReviewItem facilityError = new(ListEveryErrors) { Comment = "设备、管件、附件未连接到管线路由", Document = doc };
|
||
facilityError.ReviewElements.AddRange(errorFacilities);
|
||
if (facilityError.ReviewElements.Count > 0)
|
||
{
|
||
facilityReviews.Add(facilityError);
|
||
}
|
||
return facilityReviews;
|
||
}
|
||
/// <summary>
|
||
/// 管线坡度
|
||
/// </summary>
|
||
/// <param name="doc"></param>
|
||
/// <returns></returns>
|
||
private List<ReviewItem> ReviewMEPCurveSlope(Document doc)
|
||
{
|
||
List<ReviewItem> slopeReviews = [];
|
||
var mepCurves = doc.QueryElementsByType<MEPCurve>().Where(e => e is not InsulationLiningBase and not FlexDuct and not FlexPipe);
|
||
ReviewItem slopeError = new(ListEveryErrors) { Comment = "管线坡度有误", Document = doc };
|
||
|
||
foreach (var mepCurve in mepCurves)
|
||
{
|
||
var isError = false;
|
||
if (mepCurve is Pipe)
|
||
{
|
||
var param = mepCurve.get_Parameter(BuiltInParameter.RBS_PIPE_SLOPE);
|
||
isError = param.HasValue && ((param.AsDouble() < 0.0025 && param.AsDouble() > 10E-6) || param.AsDouble() > Math.PI); //坡度过大或过小时
|
||
}
|
||
else if (mepCurve is Duct)
|
||
{
|
||
var param = mepCurve.get_Parameter(BuiltInParameter.RBS_DUCT_SLOPE);
|
||
isError = param.HasValue && ((param.AsDouble() < 0.0025 && param.AsDouble() > 10E-6) || param.AsDouble() > Math.PI); //坡度过大或过小时
|
||
}
|
||
else if (mepCurve is Conduit or CableTray)
|
||
{
|
||
var p1 = mepCurve.get_Parameter(BuiltInParameter.RBS_START_OFFSET_PARAM).AsDouble();
|
||
var p2 = mepCurve.get_Parameter(BuiltInParameter.RBS_END_OFFSET_PARAM).AsDouble();
|
||
var l = mepCurve.get_Parameter(BuiltInParameter.CURVE_ELEM_LENGTH).AsDouble();
|
||
var sin = Math.Abs(p1 - p2) / l;
|
||
var radian = Math.Asin(sin);
|
||
var slope = Math.Tan(radian);
|
||
isError = slope is (> 10E-8 and < 0.25) || (slope > Math.PI && Math.Abs(radian - Math.PI / 2) > 10E-8);
|
||
}
|
||
|
||
if (isError)
|
||
{
|
||
slopeError.ReviewElements.Add(mepCurve);
|
||
}
|
||
}
|
||
if (slopeError.ReviewElements.Count > 0)
|
||
{
|
||
slopeReviews.Add(slopeError);
|
||
}
|
||
return slopeReviews;
|
||
}
|
||
/// <summary>
|
||
/// 颜色
|
||
/// </summary>
|
||
/// <param name="doc"></param>
|
||
/// <returns></returns>
|
||
private List<ReviewItem> ReviewInstanceColor(Document doc)
|
||
{
|
||
return [];
|
||
}
|
||
/// <summary>
|
||
/// 模型定位
|
||
/// </summary>
|
||
/// <param name="doc"></param>
|
||
/// <returns></returns>
|
||
private List<ReviewItem> ReviewLocation(Document doc)
|
||
{
|
||
var locationReviews = new List<ReviewItem>();
|
||
ReviewItem surveyPointError = new(ListEveryErrors) { Comment = "测量点坐标有误", Document = doc };
|
||
ReviewItem projectBasePointError = new(ListEveryErrors) { Comment = "项目基点坐标有误", Document = doc };
|
||
|
||
var basePoints = doc.QueryElementsByType<BasePoint>().OfType<BasePoint>().Where(p => p.get_BoundingBox(null) != null);
|
||
foreach (var point in basePoints)
|
||
{
|
||
//南北
|
||
var ns = Math.Round(
|
||
point.get_Parameter(BuiltInParameter.BASEPOINT_NORTHSOUTH_PARAM).AsDouble() * 304.8,
|
||
0,
|
||
MidpointRounding.AwayFromZero
|
||
);
|
||
//东西
|
||
var ew = Math.Round(
|
||
point.get_Parameter(BuiltInParameter.BASEPOINT_EASTWEST_PARAM).AsDouble() * 304.8,
|
||
0,
|
||
MidpointRounding.AwayFromZero
|
||
);
|
||
//建筑零标高的绝对高程
|
||
var height = Math.Round(
|
||
point.get_Parameter(BuiltInParameter.BASEPOINT_ELEVATION_PARAM).AsDouble() * 304.8,
|
||
0,
|
||
MidpointRounding.AwayFromZero
|
||
);
|
||
//测量点
|
||
if (point.IsShared)
|
||
{
|
||
if (ns != 0.0 && ew != 0.0)
|
||
{
|
||
surveyPointError.ReviewElements.Add(point);
|
||
}
|
||
}
|
||
else
|
||
{
|
||
//小数点的位数要准确,mm级单位下,南北的坐标位数10位(24、25开头),东西方向坐标位数9位。(48、49开头)
|
||
var nsStr = ((long)Math.Floor(ns)).ToString();
|
||
var ewStr = ((long)Math.Floor(ew)).ToString();
|
||
var nsCount = nsStr.Length;
|
||
var ewCount = ewStr.Length;
|
||
if ((nsCount != 10) || !(nsStr.StartsWith("24") || nsStr.StartsWith("25")))
|
||
{
|
||
projectBasePointError.ReviewElements.Add(point);
|
||
}
|
||
//存在超出深圳市范围的轨道交通站点
|
||
if (ewCount != 9 ||
|
||
!(ewStr.StartsWith("48") ||
|
||
ewStr.StartsWith("49") ||
|
||
ewStr.StartsWith("50") ||
|
||
ewStr.StartsWith("51") ||
|
||
ewStr.StartsWith("52")))
|
||
{
|
||
if (!projectBasePointError.ReviewElements.Contains(point))
|
||
{
|
||
projectBasePointError.ReviewElements.Add(point);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
if (surveyPointError.ReviewElements.Count > 0)
|
||
{
|
||
locationReviews.Add(surveyPointError);
|
||
}
|
||
if (projectBasePointError.ReviewElements.Count > 0)
|
||
{
|
||
locationReviews.Add(projectBasePointError);
|
||
}
|
||
return locationReviews;
|
||
}
|
||
/// <summary>
|
||
/// 使用包围盒筛选器查找与目标管道可能重叠的管道。
|
||
/// </summary>
|
||
/// <param name="doc">Revit文档</param>
|
||
/// <param name="mepCurve">要检查的目标管道</param>
|
||
/// <returns>可能重叠的管道ID列表</returns>
|
||
public IList<Element> FindOverlapsWithBoundingBox(MEPCurve mepCurve)
|
||
{
|
||
var doc = mepCurve.Document;
|
||
// 获取目标管道的包围盒
|
||
BoundingBoxXYZ bb = mepCurve.get_BoundingBox(null);
|
||
if (bb == null)
|
||
{
|
||
return [];
|
||
}
|
||
|
||
// 创建一个轮廓线作为过滤器
|
||
Outline outline = new Outline(bb.Min, bb.Max);
|
||
|
||
// 创建包围盒相交过滤器
|
||
var bbFilter = new BoundingBoxIsInsideFilter(outline, 100 / 304.8);
|
||
|
||
// 排除目标管道自身
|
||
ElementId targetId = mepCurve.Id;
|
||
ExclusionFilter exclusionFilter = new ExclusionFilter(new List<ElementId> { targetId });
|
||
|
||
// 筛选出所有与目标管道包围盒相交的管道
|
||
FilteredElementCollector collector = new FilteredElementCollector(doc)
|
||
.OfClass(typeof(MEPCurve))
|
||
.WherePasses(bbFilter)
|
||
.WherePasses(exclusionFilter);
|
||
|
||
return collector.ToElements();
|
||
}
|
||
/// <summary>
|
||
/// 管线连接
|
||
/// </summary>
|
||
/// <returns></returns>
|
||
private List<ReviewItem> ReviewMEPCurveConnection(Document doc)
|
||
{
|
||
var mepCurveConnectionReviews = new List<ReviewItem>(2);
|
||
var mepCurves = doc.OfType<MEPCurve>()
|
||
.Where(e => e is not InsulationLiningBase && (e is not Pipe pipe || pipe.Diameter > 50 / 304.8))
|
||
.ToList();
|
||
ReviewItem singleMEPCurveError = new(ListEveryErrors) { Comment = "存在未连接的孤立管线", Document = doc };
|
||
ReviewItem equalMEPCurveError = new(ListEveryErrors) { Comment = "机电管线存在重叠", Document = doc };
|
||
foreach (var c1 in mepCurves)
|
||
{
|
||
//var length = c1.get_Parameter(BuiltInParameter.CURVE_ELEM_LENGTH).AsDouble();
|
||
var connectors = c1.GetConnectors(true);
|
||
if (connectors.Size == 2)
|
||
{
|
||
singleMEPCurveError.ReviewElements.Add(c1);
|
||
}
|
||
var loc = c1.GetLocCurve();
|
||
var others = FindOverlapsWithBoundingBox(c1);
|
||
foreach (var c2 in others)
|
||
{
|
||
var result = loc.Intersect(c2.GetLocCurve());
|
||
if (result == SetComparisonResult.Equal)
|
||
{
|
||
equalMEPCurveError.ReviewElements.Add(c1);
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
//return mepCurveConnectionReviews;
|
||
|
||
if (singleMEPCurveError.ReviewElements.Count > 0)
|
||
{
|
||
mepCurveConnectionReviews.Add(singleMEPCurveError);
|
||
}
|
||
if (equalMEPCurveError.ReviewElements.Count > 0)
|
||
{
|
||
mepCurveConnectionReviews.Add(equalMEPCurveError);
|
||
}
|
||
return mepCurveConnectionReviews;
|
||
}
|
||
/// <summary>
|
||
/// 审核模型
|
||
/// </summary>
|
||
[RelayCommand(CanExecute = nameof(CanCheck))]
|
||
private async Task ReviewModel()
|
||
{
|
||
if (uiapp.ActiveUIDocument == null)
|
||
{
|
||
return;
|
||
}
|
||
if (!Directory.Exists(OutputFolder))
|
||
{
|
||
Directory.CreateDirectory(OutputFolder);
|
||
}
|
||
var message = new StringBuilder(1024);
|
||
|
||
var doc = uiapp.ActiveUIDocument.Document;
|
||
var path = doc.PathName;
|
||
if (doc.IsFamilyDocument)
|
||
{
|
||
MessageBox.Show("当前文件不是项目文件!", "提示", MessageBoxButton.OK, MessageBoxImage.Information);
|
||
return;
|
||
}
|
||
if (string.IsNullOrEmpty(path))
|
||
{
|
||
MessageBox.Show("当前文件未保存,请先保存文件!", "提示", MessageBoxButton.OK, MessageBoxImage.Information);
|
||
return;
|
||
}
|
||
var rvtfileName = Path.GetFileNameWithoutExtension(path);
|
||
var docxFileName = Path.Combine($"{OutputFolder}", $"{rvtfileName}.docx");
|
||
|
||
var result = await ReviewDocumentAndSaveDocx(doc, message);
|
||
if (result)
|
||
{
|
||
// var dialogResult = message.Length > 0
|
||
//? MessageBox.Show($"{message}\n是否打开输出的文件夹?", "审核完成", MessageBoxButton.OK, MessageBoxImage.Information)
|
||
//: MessageBox.Show("是否打开输出的文件夹?", "审核完成", MessageBoxButton.YesNo, MessageBoxImage.Information);
|
||
// if (dialogResult == MessageBoxResult.Yes)
|
||
// {
|
||
|
||
// WinDialogAssists.OpenFolderAndSelectFile(docxFileName);
|
||
// //Process.Start(new ProcessStartInfo(OutputFolder) { UseShellExecute = true });
|
||
// }
|
||
OpenTargetFolder(message, docxFileName);
|
||
}
|
||
|
||
}
|
||
/// <summary>
|
||
/// 打开目标文件夹
|
||
/// </summary>
|
||
/// <param name="message"></param>
|
||
private static void OpenTargetFolder(StringBuilder message, string fileOrFolderPath)
|
||
{
|
||
var result = message.Length > 0
|
||
? MessageBox.Show($"{message}\n是否打开输出的文件夹?", "审核完成", MessageBoxButton.OK, MessageBoxImage.Information)
|
||
: MessageBox.Show("是否打开输出的文件夹?", "审核完成", MessageBoxButton.YesNo, MessageBoxImage.Information);
|
||
|
||
if (result == MessageBoxResult.Yes)
|
||
{
|
||
if (File.Exists(fileOrFolderPath))
|
||
{
|
||
WinDialogAssists.OpenFolderAndSelectFile(fileOrFolderPath);
|
||
}
|
||
else
|
||
{
|
||
Process.Start(new ProcessStartInfo(fileOrFolderPath));
|
||
}
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 多选审核模型
|
||
/// </summary>
|
||
[RelayCommand(CanExecute = nameof(CanCheck))]
|
||
private async Task ReviewModels()
|
||
{
|
||
OpenFileDialog dialog =
|
||
new()
|
||
{
|
||
Filter = "Revit项目文件(*.rvt)|*.rvt",
|
||
Multiselect = true,
|
||
Title = "选择审核文件",
|
||
};
|
||
|
||
StringBuilder message = new(1024);
|
||
if (dialog.ShowDialog() == true)
|
||
{
|
||
if (!Directory.Exists(OutputFolder))
|
||
{
|
||
Directory.CreateDirectory(OutputFolder);
|
||
}
|
||
foreach (var path in dialog.FileNames)
|
||
{
|
||
Document doc;
|
||
var fileName = Path.GetFileNameWithoutExtension(path);
|
||
try
|
||
{
|
||
doc = uiapp.Application.OpenDocumentFile(path);
|
||
}
|
||
catch (Autodesk.Revit.Exceptions.FileAccessException)
|
||
{
|
||
message.AppendLine($"模型文件:{fileName} 被保存为更新的版本,无法打开");
|
||
continue;
|
||
}
|
||
catch (Autodesk.Revit.Exceptions.CorruptModelException)
|
||
{
|
||
message.AppendLine($"模型文件:{fileName},打开文档失败或文档损坏");
|
||
continue;
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
message.AppendLine($"模型文件:{fileName},{ex.Message}");
|
||
continue;
|
||
}
|
||
if (doc.IsFamilyDocument)
|
||
{
|
||
message.AppendLine($"模型文件:{fileName} 不是项目文件");
|
||
doc.Close(false);
|
||
continue;
|
||
}
|
||
await ReviewDocumentAndSaveDocx(doc, message);
|
||
doc.Close(false);
|
||
}
|
||
OpenTargetFolder(message, OutputFolder);
|
||
}
|
||
}
|
||
/// <summary>
|
||
/// 构件命名
|
||
/// </summary>
|
||
/// <param name="doc"></param>
|
||
/// <returns></returns>
|
||
private List<ReviewItem> ReviewName(Document doc)
|
||
{
|
||
var nameReviews = new List<ReviewItem>();
|
||
ReviewItem prefixError = new(ListEveryErrors)
|
||
{
|
||
Comment = "专业缩写前缀有误",
|
||
Document = doc
|
||
};
|
||
ReviewItem segmentError = new(ListEveryErrors)
|
||
{
|
||
Comment = "类型名称不符合三段式",
|
||
Document = doc
|
||
};
|
||
var elements = doc.OfParentModelCollector();
|
||
var typeInstancesGroups =
|
||
elements.GroupBy(e => e.GetTypeId()).ToList();
|
||
foreach (var item in typeInstancesGroups)
|
||
{
|
||
var id = item.Key;
|
||
if (id == null || id == ElementId.InvalidElementId)
|
||
{
|
||
continue;
|
||
}
|
||
var type = doc.GetElement(id);
|
||
//去掉没有类型的实例,如地形等
|
||
if (type == null)
|
||
{
|
||
continue;
|
||
}
|
||
var array = type.Name.Split('-');
|
||
var i = disc.Contains("场布") || disc.Contains("场地布置") ? 2 : 3;
|
||
if (array.Length >= i)
|
||
{
|
||
var prefix = array[0];
|
||
if (
|
||
prefix
|
||
is "QQ"
|
||
or "YT"
|
||
or "XL"
|
||
or "GJ"
|
||
or "CL"
|
||
or "JZ"
|
||
or "JG"
|
||
or "GP"
|
||
or "DZ"
|
||
or "TF"
|
||
or "GD"
|
||
or "TX"
|
||
or "XH"
|
||
or "ZS"
|
||
or "HB"
|
||
or "ZJ"
|
||
or "HJ"
|
||
or "CX"
|
||
or "MJ"
|
||
or "YK"
|
||
or "ZK"
|
||
or "ZT"
|
||
or "CJ"
|
||
or "XX"
|
||
or "TC"
|
||
or "NY"
|
||
or "ZB"
|
||
or "AF"
|
||
or "CD"//场布
|
||
)
|
||
{
|
||
continue;
|
||
}
|
||
prefixError.ReviewElements.Add(type);
|
||
//prefixError.Append($"{type.Name}");
|
||
}
|
||
else
|
||
{
|
||
segmentError.ReviewElements.Add(type);
|
||
}
|
||
}
|
||
if (prefixError.ReviewElements.Count > 0)
|
||
{
|
||
nameReviews.Add(prefixError);
|
||
}
|
||
if (segmentError.ReviewElements.Count > 0)
|
||
{
|
||
nameReviews.Add(segmentError);
|
||
}
|
||
return nameReviews;
|
||
}
|
||
/// <summary>
|
||
/// 属性条目
|
||
/// </summary>
|
||
/// <param name="doc"></param>
|
||
/// <returns></returns>
|
||
private List<ReviewItem> ReviewProperties(Document doc)
|
||
{
|
||
var propertiesReviews = new List<ReviewItem>();
|
||
ReviewItem paramCountError = new(ListEveryErrors) { Comment = "构件信息条目不全或重复添加", Document = doc };
|
||
var detail = "模型权属单位、创建单位、创建人员及联系方式、校审单位等信息,部分或全部未填写。";
|
||
|
||
ReviewItem paramFillError = new(ListEveryErrors)
|
||
{
|
||
Comment = "要求的构件信息未填写",
|
||
Document = doc,
|
||
DetailNote = detail,
|
||
};
|
||
ReviewItem belongFloorpParamFillError = default;
|
||
if (BelongFloor)
|
||
{
|
||
belongFloorpParamFillError = new(ListEveryErrors)
|
||
{
|
||
Comment = "“所属楼层”信息未填写",
|
||
Document = doc,
|
||
DetailNote = "所属楼层信息未填写",
|
||
};
|
||
}
|
||
ReviewItem szComponentParamFillError = default;
|
||
|
||
if (SzComponent)
|
||
{
|
||
szComponentParamFillError = new(ListEveryErrors)
|
||
{
|
||
Comment = "“深圳构件标识”信息未填写",
|
||
Document = doc,
|
||
DetailNote = "构件的深圳构件标识信息未填写",
|
||
};
|
||
}
|
||
|
||
ReviewItem projectInfoCountError = new(ListEveryErrors) { Comment = "要求的项目信息条目不全或重复添加", Document = doc };
|
||
ReviewItem projectInfoFillError = new(ListEveryErrors) { Comment = "要求的项目信息未填写", Document = doc };
|
||
//ReviewItem floorError = new() { Comment = "信息丢失或未填写", doc = doc };
|
||
|
||
var elements = doc.OfParentModelCollector();
|
||
var typeInstancesGroups = elements
|
||
.GroupBy(e => e.GetTypeId())
|
||
.ToList();
|
||
foreach (var group in typeInstancesGroups)
|
||
{
|
||
var paramCount = 0;
|
||
var typeId = group.Key;
|
||
if (typeId == null || typeId == ElementId.InvalidElementId)
|
||
{
|
||
continue;
|
||
}
|
||
|
||
var type = doc.GetElement(typeId);
|
||
var typeParams = type.Parameters
|
||
.OfType<Parameter>()
|
||
.Distinct(new PropertyComparer<Parameter>(p => p.Definition.Name));
|
||
//类型参数个数
|
||
foreach (var param in typeParams)
|
||
{
|
||
var array = param.Definition.Name.Split('-');
|
||
if (array.Length >= 3)
|
||
{
|
||
var prefix = array[0].Trim();
|
||
if (prefix is ("ID" or "LC" or "ST" or "GJ" or "MF" or "AM" or "FM" or "TM" or "CM"))
|
||
{
|
||
paramCount++;
|
||
}
|
||
}
|
||
}
|
||
////是否有类型参数
|
||
//var floorParam = type.GetParameters("所属楼层").FirstOrDefault();
|
||
//var belongCom = type.GetParameters("AM-200-模型权属单位").FirstOrDefault();
|
||
//var assetCom = type.GetParameters("AM-200-资产权属单位").FirstOrDefault();
|
||
//var creatorCom = type.GetParameters("CM-100-创建单位").FirstOrDefault();
|
||
//var creator = type.GetParameters("CM-100-创建人员及联系方式").FirstOrDefault();
|
||
//var reviewComment = type.GetParameters("CM-100-校审单位").FirstOrDefault();
|
||
//var hasSetValues = string.IsNullOrEmpty(floorParam?.AsString()) ||
|
||
// string.IsNullOrEmpty(belongCom?.AsString()) ||
|
||
// string.IsNullOrEmpty(assetCom?.AsString()) ||
|
||
// string.IsNullOrEmpty(creatorCom?.AsString()) ||
|
||
// string.IsNullOrEmpty(creator?.AsString()) ||
|
||
// string.IsNullOrEmpty(reviewComment?.AsString());
|
||
//实例参数个数
|
||
foreach (var elem in group)
|
||
{
|
||
//var floorParam = elem.GetParameters("所属楼层").FirstOrDefault();
|
||
//var belongCom = elem.GetParameters("AM-200-模型权属单位").FirstOrDefault();
|
||
//var assetCom = elem.GetParameters("AM-200-资产权属单位").FirstOrDefault();
|
||
//var creatorCom = elem.GetParameters("CM-100-创建单位").FirstOrDefault();
|
||
//var creator = elem.GetParameters("CM-100-创建人员及联系方式").FirstOrDefault();
|
||
//var reviewComment = elem.GetParameters("CM-100-校审单位").FirstOrDefault();
|
||
//var hasSetValues = string.IsNullOrEmpty(floorParam?.AsString()) ||
|
||
// string.IsNullOrEmpty(belongCom?.AsString()) ||
|
||
// string.IsNullOrEmpty(assetCom?.AsString()) ||
|
||
// string.IsNullOrEmpty(creatorCom?.AsString()) ||
|
||
// string.IsNullOrEmpty(creator?.AsString()) ||
|
||
// string.IsNullOrEmpty(reviewComment?.AsString());
|
||
var hasSetValues =
|
||
HasSetValue(elem, "AM-200-模型权属单位") &&
|
||
HasSetValue(elem, "CM-100-创建单位") &&
|
||
HasSetValue(elem, "CM-100-创建人员及联系方式") &&
|
||
HasSetValue(elem, "CM-100-校审单位");
|
||
if (!hasSetValues)
|
||
{
|
||
paramFillError.ReviewElements.Add(elem);
|
||
}
|
||
|
||
//所属楼层
|
||
if (BelongFloor)
|
||
{
|
||
hasSetValues = HasSetValue(elem, "所属楼层");
|
||
if (!hasSetValues)
|
||
{
|
||
belongFloorpParamFillError.ReviewElements.Add(elem);
|
||
|
||
}
|
||
}
|
||
//深圳构件标识
|
||
if (SzComponent)
|
||
{
|
||
hasSetValues = HasSetValue(elem, "深圳构件标识");
|
||
if (!hasSetValues)
|
||
{
|
||
szComponentParamFillError.ReviewElements.Add(elem);
|
||
}
|
||
}
|
||
|
||
|
||
}
|
||
var instanceParams = group.FirstOrDefault().Parameters
|
||
.OfType<Parameter>()
|
||
.Distinct(new PropertyComparer<Parameter>(p => p.Definition.Name));
|
||
foreach (var param in instanceParams)
|
||
{
|
||
var array = param.Definition.Name.Split('-');
|
||
if (array.Length >= 3)
|
||
{
|
||
var prefix = array[0].Trim();
|
||
//TC-是技术参数
|
||
if (prefix is ("ID" or "LC" or "ST" or "GJ" or "MF" or "AM" or "FM" or "TM" or "CM"))
|
||
{
|
||
paramCount++;
|
||
}
|
||
}
|
||
}
|
||
//参数数量有误
|
||
if (paramCount < 60)
|
||
{
|
||
paramCountError.ReviewElements.Add(type);
|
||
}
|
||
}
|
||
//项目信息有误
|
||
var projectParamCoutHasError = false;
|
||
var projectParamCount = 0;
|
||
var projectParams = doc.ProjectInformation.Parameters.OfType<Parameter>().Distinct(new PropertyComparer<Parameter>(p => p.Definition.Name));
|
||
foreach (var param in projectParams)
|
||
{
|
||
if (param.Id.IntegerValue > 0)
|
||
{
|
||
var name = param.Definition.Name;
|
||
if (name.StartsWith("项目标识") ||
|
||
name.StartsWith("坐标定位") ||
|
||
name.StartsWith("项目内部定位") ||
|
||
name.StartsWith("建筑类别或等级") ||
|
||
name.StartsWith("建设说明") || name.StartsWith("系统分类"))
|
||
{
|
||
projectParamCount++;
|
||
}
|
||
}
|
||
}
|
||
if (IsCivil)
|
||
{
|
||
if (projectParamCount < 17)
|
||
{
|
||
projectInfoCountError.DetailNote = "请根据《轨道交通工程BIM模型建模标准》添加项目标识、项目定位、建设说明、建筑类别或等级等信息条目,格式为:属性组-属性名称。";
|
||
projectParamCoutHasError = true;
|
||
//projectInfoCountError.ReviewElements.Add(doc.ProjectInformation);
|
||
}
|
||
}
|
||
else
|
||
{
|
||
if (projectParamCount < 6)
|
||
{
|
||
projectInfoCountError.DetailNote = "请根据《轨道交通工程BIM模型建模标准》添加项目标识、系统分类等信息条目,格式为:属性组-属性名称。";
|
||
projectParamCoutHasError = true;
|
||
}
|
||
}
|
||
|
||
var projectName = doc.ProjectInformation.GetParameters("项目标识-项目名称").FirstOrDefault()?.AsString();
|
||
var projectInfoFillHasError = string.IsNullOrEmpty(projectName);
|
||
//项目参数信息未填写
|
||
if (IsCivil)
|
||
{
|
||
var stationName = doc.ProjectInformation.GetParameters("项目标识-站点名称").FirstOrDefault()?.AsString();
|
||
var engineerName = doc.ProjectInformation.GetParameters("项目标识-工程名称").FirstOrDefault()?.AsString();
|
||
var startMileage = doc.ProjectInformation.GetParameters("坐标定位-起点里程").FirstOrDefault()?.AsString();
|
||
var centerMileage = doc.ProjectInformation.GetParameters("坐标定位-站台中心里程").FirstOrDefault()?.AsString();
|
||
var location = doc.ProjectInformation.GetParameters("建设说明-建设地点").FirstOrDefault()?.AsString();
|
||
var phase = doc.ProjectInformation.GetParameters("建设说明-建设阶段").FirstOrDefault()?.AsString();
|
||
var archiLevel = doc.ProjectInformation.GetParameters("建筑类别或等级-建筑等级").FirstOrDefault()?.AsString();
|
||
var seismicGrade = doc.ProjectInformation.GetParameters("建筑类别或等级-抗震等级").FirstOrDefault()?.AsString();
|
||
var seismicIntensity = doc.ProjectInformation.GetParameters("建筑类别或等级-抗震设防烈度").FirstOrDefault()?.AsString();
|
||
projectInfoFillHasError = projectInfoFillHasError && string.IsNullOrEmpty(stationName) ||
|
||
string.IsNullOrEmpty(engineerName) ||
|
||
string.IsNullOrEmpty(startMileage) ||
|
||
string.IsNullOrEmpty(centerMileage) ||
|
||
string.IsNullOrEmpty(location) ||
|
||
string.IsNullOrEmpty(phase) ||
|
||
string.IsNullOrEmpty(archiLevel) ||
|
||
string.IsNullOrEmpty(seismicGrade) ||
|
||
string.IsNullOrEmpty(seismicIntensity);
|
||
projectInfoFillError.DetailNote = "项目名称、站点名称、工程名称、起点里程、站台中心里程、建设地点、建设阶段、建筑等级、抗震等级、抗震设防烈度等项目信息未填写。";
|
||
}
|
||
else
|
||
{
|
||
var systemName = doc.ProjectInformation.GetParameters("系统分类-系统分类名称").FirstOrDefault()?.AsString();
|
||
var systemCode = doc.ProjectInformation.GetParameters("系统分类-系统分类代号").FirstOrDefault()?.AsString();
|
||
projectInfoFillHasError = projectInfoFillHasError && string.IsNullOrEmpty(systemName) || string.IsNullOrEmpty(systemCode);
|
||
projectInfoFillError.DetailNote = $"项目名称、系统分类名称、系统分类代号等部分项目信息未填写";
|
||
}
|
||
|
||
if (projectParamCoutHasError)
|
||
{
|
||
propertiesReviews.Add(projectInfoCountError);
|
||
}
|
||
|
||
if (projectInfoFillHasError)
|
||
{
|
||
propertiesReviews.Add(projectInfoFillError);
|
||
}
|
||
if (paramCountError.ReviewElements.Count > 0)
|
||
{
|
||
propertiesReviews.Add(paramCountError);
|
||
}
|
||
if (paramFillError.ReviewElements.Count > 0)
|
||
{
|
||
propertiesReviews.Add(paramFillError);
|
||
}
|
||
|
||
if (BelongFloor && belongFloorpParamFillError.ReviewElements.Count > 0)
|
||
{
|
||
propertiesReviews.Add(belongFloorpParamFillError);
|
||
}
|
||
|
||
if (SzComponent && szComponentParamFillError.ReviewElements.Count > 0)
|
||
{
|
||
propertiesReviews.Add(szComponentParamFillError);
|
||
}
|
||
|
||
//if (floorError.ReviewElements.Count > 0)
|
||
//{
|
||
// propertiesReviews.Add(floorError);
|
||
//}
|
||
return propertiesReviews;
|
||
}
|
||
/// <summary>
|
||
/// 是否存在已填值的该参数
|
||
/// </summary>
|
||
/// <param name="elem"></param>
|
||
/// <param name="paramName"></param>
|
||
private static bool HasSetValue(Element elem, string paramName)
|
||
{
|
||
var parameters = elem.GetParameters(paramName);
|
||
if (parameters.Count == 0)
|
||
{
|
||
return false;
|
||
}
|
||
else if (parameters.Count == 1)
|
||
{
|
||
return !string.IsNullOrEmpty(parameters.FirstOrDefault().AsString()) && !string.IsNullOrWhiteSpace(parameters.FirstOrDefault().AsString());
|
||
}
|
||
else
|
||
{
|
||
foreach (var param in parameters)
|
||
{
|
||
if (!string.IsNullOrEmpty(param.AsString()) && !string.IsNullOrWhiteSpace(param.AsString()))
|
||
{
|
||
return true;
|
||
}
|
||
}
|
||
}
|
||
|
||
return false;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 选择输出目录
|
||
/// </summary>
|
||
/// <param name="window"></param>
|
||
[RelayCommand]
|
||
private void SelectOutputFolder(Window window)
|
||
{
|
||
FolderBrowserDialog dialog = new();
|
||
if (dialog.ShowDialog(window))
|
||
{
|
||
OutputFolder = dialog.SelectedPath;
|
||
}
|
||
}
|
||
|
||
private bool CanCheck => !string.IsNullOrEmpty(Reviewer) &&
|
||
!string.IsNullOrEmpty(OutputFolder) &&
|
||
!string.IsNullOrEmpty(Number);
|
||
}
|
||
|
||
class ReviewItem(bool listErrors)
|
||
{
|
||
public override string ToString()
|
||
{
|
||
var sb = new StringBuilder();
|
||
sb.Append($"{Comment}");
|
||
sb.AppendLine(ReviewElements.Count > 0 ? "{族名:类型名或实例名:类型或实例Id}:" : ":");
|
||
if (!string.IsNullOrEmpty(DetailNote)/* && ReviewElements.Count > 50*/)
|
||
{
|
||
sb.AppendLine(DetailNote);
|
||
//return sb.ToString();
|
||
}
|
||
if (ReviewElements.Count > 1000 && !ListErrors)
|
||
{
|
||
var group = ReviewElements.GroupBy(e => e.GetTypeId())
|
||
.Select(g => g.FirstOrDefault().Name)
|
||
.Aggregate((n1, n2) => $"{n1};{n2}");
|
||
sb.Replace("{族名:类型名或实例名:类型或实例Id}", string.Empty);
|
||
sb.AppendLine($"类型名称:{group}");
|
||
sb.Append("注:问题构件数量超过1000个,未全部列举,请自行检查。");
|
||
return sb.ToString();
|
||
}
|
||
foreach (var element in ReviewElements)
|
||
{
|
||
var doc = element.Document;
|
||
sb.Append('{');
|
||
if (element is ElementType elementType)
|
||
{
|
||
var familyName = elementType.FamilyName;
|
||
sb.Append(familyName);
|
||
if (!string.IsNullOrEmpty(familyName))
|
||
{
|
||
sb.Append(':');
|
||
}
|
||
var typeName = element.Name;
|
||
if (!string.IsNullOrEmpty(typeName) && typeName != familyName)
|
||
{
|
||
sb.Append(typeName).Append(':');
|
||
}
|
||
sb.Append(element.Id.IntegerValue);
|
||
}
|
||
else
|
||
{
|
||
if (element.GetTypeId() != ElementId.InvalidElementId)
|
||
{
|
||
var type = doc.GetElement(element.GetTypeId());
|
||
if (type is ElementType elemType)
|
||
{
|
||
var familyName = elemType.FamilyName;
|
||
sb.Append(familyName);
|
||
if (!string.IsNullOrEmpty(familyName))
|
||
{
|
||
sb.Append(':');
|
||
}
|
||
var typeName = element.Name;
|
||
sb.Append(typeName);
|
||
if (!string.IsNullOrEmpty(typeName))
|
||
{
|
||
sb.Append(':');
|
||
}
|
||
var elemName = element.Name;
|
||
if (elemName != typeName && !string.IsNullOrEmpty(elemName))
|
||
{
|
||
sb.Append(element.Name).Append(':');
|
||
}
|
||
sb.Append(element.Id.IntegerValue);
|
||
|
||
}
|
||
}
|
||
else
|
||
{
|
||
var elemName = element.Name;
|
||
sb.Append(element.Name);
|
||
if (!string.IsNullOrEmpty(elemName))
|
||
{
|
||
sb.Append(':');
|
||
}
|
||
sb.Append(element.Id.IntegerValue);
|
||
}
|
||
//sb.Append($"{element.Name}");
|
||
//if (!string.IsNullOrEmpty(element.Name))
|
||
//{
|
||
// sb.Append(':');
|
||
//}
|
||
//sb.Append($"{element.Id}");
|
||
}
|
||
sb.Append("};");
|
||
}
|
||
sb.AppendLine();
|
||
return sb.ToString();
|
||
}
|
||
|
||
public ReviewCategory Category { get; set; }
|
||
public string Comment { get; set; }
|
||
public Document Document { get; set; }
|
||
|
||
public string ModelFileName => Path.GetFileNameWithoutExtension(Document.Title);
|
||
public int Number { get; set; }
|
||
/// <summary>
|
||
/// 复审意见
|
||
/// </summary>
|
||
public string ReexamineComment { get; set; }
|
||
/// <summary>
|
||
/// 意见回复
|
||
/// </summary>
|
||
public string ReplyToComment { get; set; }
|
||
/// <summary>
|
||
/// 详细意见备注
|
||
/// </summary>
|
||
public string DetailNote { get; set; }
|
||
/// <summary>
|
||
/// 元素、元素类型、族等
|
||
/// </summary>
|
||
public List<Element> ReviewElements { get; set; } = [];
|
||
public bool ListErrors { get; set; } = listErrors;
|
||
}
|
||
enum ReviewCategory
|
||
{
|
||
None = 0,
|
||
Standard = 1,
|
||
Accuracy = 2,
|
||
Optional = 3,
|
||
}
|
||
public class PropertyComparer<T> : IEqualityComparer<T>
|
||
{
|
||
private readonly Func<T, object> _propertySelector;
|
||
|
||
public PropertyComparer(Func<T, object> propertySelector)
|
||
{
|
||
_propertySelector = propertySelector;
|
||
}
|
||
|
||
public bool Equals(T x, T y)
|
||
{
|
||
return _propertySelector(x).Equals(_propertySelector(y));
|
||
}
|
||
|
||
public int GetHashCode(T obj)
|
||
{
|
||
return _propertySelector(obj).GetHashCode();
|
||
}
|
||
}
|
||
}
|