feat: 同步全部系统改动与文档

This commit is contained in:
2026-07-28 10:29:56 +08:00
parent 62f7632fc6
commit 1fa1ece06d
487 changed files with 267645 additions and 144 deletions

View File

@@ -0,0 +1,420 @@
param([string]$ScriptRootOverride = '')
$ErrorActionPreference = 'Stop'
$scriptRootPath = if ($ScriptRootOverride) { $ScriptRootOverride } else { $PSScriptRoot }
$root = (Resolve-Path (Join-Path $scriptRootPath '..\..')).Path
$catalogPath = Join-Path $scriptRootPath 'full-page-catalog.json'
$screenshots = Join-Path $scriptRootPath 'full-screenshots'
$docDir = Join-Path $root 'doc'
$desktop = [Environment]::GetFolderPath('Desktop')
$sourceDocx = Join-Path $docDir '大连元利流体技术有限公司MES系统操作手册_系统管理篇_样稿.docx'
$fileName = '大连元利流体技术有限公司MES系统用户操作手册_V1.1_2026-07-23'
$docxPath = Join-Path $docDir ($fileName + '.docx')
$pdfPath = Join-Path $docDir ($fileName + '.pdf')
$desktopDocx = Join-Path $desktop ($fileName + '.docx')
$desktopPdf = Join-Path $desktop ($fileName + '.pdf')
$progressPath = Join-Path ([IO.Path]::GetTempPath()) 'mes-full-manual-build.log'
[IO.File]::WriteAllText($progressPath, "开始生成 $(Get-Date -Format 'yyyy-MM-dd HH:mm:ss')`r`n", [Text.Encoding]::UTF8)
function Write-BuildProgress {
param([string]$Message)
[IO.File]::AppendAllText($progressPath, "$(Get-Date -Format 'HH:mm:ss') $Message`r`n", [Text.Encoding]::UTF8)
}
if (-not (Test-Path -LiteralPath $sourceDocx)) { throw "系统管理篇源文档不存在:$sourceDocx" }
if (-not (Test-Path -LiteralPath $catalogPath)) { throw "页面目录不存在:$catalogPath" }
$catalog = Get-Content -Raw -Encoding UTF8 $catalogPath | ConvertFrom-Json
if ($catalog.Length -ne 69) { throw "页面目录数量应为69实际为$($catalog.Length)" }
foreach ($page in $catalog) {
$imagePath = Join-Path $screenshots $page.screenshot
if (-not (Test-Path -LiteralPath $imagePath)) { throw "页面截图不存在:$imagePath" }
}
New-Item -ItemType Directory -Force -Path $docDir | Out-Null
$moduleChapters = [ordered]@{
'设备管理' = 7
'研发管理' = 8
'工艺管理' = 9
'计划排产' = 10
'生产管理' = 11
'异常提醒' = 12
'质量管理' = 13
'销售订单管理' = 14
}
function Join-Values {
param([object]$Values, [int]$Limit = 12)
if ($null -eq $Values -or $Values.Length -eq 0) { return '无' }
$items = @($Values | Select-Object -First $Limit)
$text = $items -join '、'
if ($Values.Length -gt $Limit) { $text += "$($Values.Length)" }
return $text
}
function Get-FilterType {
param([string]$Field)
if ($Field -match '日期|时间|开始|结束|完工|入账') { return '日期/时间或范围选择' }
if ($Field -match '状态|类型|类别|属性|性质|结果|部门|工位|工序|人员|指派|来源|班次|齐套|送检|入库|发货') { return '下拉、开关或选项选择' }
if ($Field -match '编码|编号|订单|任务ID|计划号|合同号|图号|名称|操作人|物料|产品') { return '文本输入或远程选择' }
return '输入或选择控件'
}
function Get-FilterGuide {
param([string]$Field)
if ($Field -match '日期|时间|开始|结束|完工|入账') { return '选择起止时间时按区间过滤;清空表示不限制该时间条件。' }
if ($Field -match '状态|类型|类别|属性|性质|结果|部门|工位|工序|人员|指派|来源|班次|齐套|送检|入库|发货') { return '从页面提供的选项中选择;“全部”或清空通常表示不限制该条件。' }
if ($Field -match '编码|编号|订单|任务ID|计划号|合同号|图号|名称|操作人|物料|产品') { return '输入关键值或从远程候选项中选择,再与其他已填写条件共同查询。' }
return '按页面控件输入或选择,点击【查询】后与其他条件共同生效。'
}
function Get-ActionGuide {
param([string]$Button)
if ($Button -match '查询|搜索|刷新') { return '读取当前筛选条件并刷新列表,不写入业务数据。' }
if ($Button -match '导出|Excel|下载') { return '将当前查询或当前页签结果导出/下载;导出前先确认筛选范围。' }
if ($Button -match '查看|预览|明细|记录|图纸|附件') { return '打开当前记录的只读详情、历史、图纸或附件;关闭窗口返回列表。' }
if ($Button -match '新增|新建|添加|设备$') { return '打开新增窗口,填写必填项并确认后写入;正式操作前检查编码和关联主数据。' }
if ($Button -match '编辑|修改|修正') { return '选择目标记录后打开编辑窗口,核对影响范围并保存修改。' }
if ($Button -match '删除|移除|作废|关闭订单') { return '执行不可逆或影响状态的操作;必须核对下游占用并在确认框中再次确认。' }
if ($Button -match '取消|关闭$|返回') { return '关闭当前窗口或放弃本次未提交内容,通常不写入数据。' }
if ($Button -match '保存|确定|提交|确认') { return '提交当前表单或选择结果;提交前复核必填项、数量、时间和目标记录。' }
if ($Button -match '批量|排产|下发|派工|指派') { return '先选择目标行并完整填写工位、人员、数量和时间,再批量生成或下发任务。' }
if ($Button -match '开始|开工|暂停|结束|完成|报工|送检|收验|收检|发料|收料|领料|退库') { return '改变任务或物料流转状态并形成操作记录;只对已核对的当前任务执行。' }
if ($Button -match '上传') { return '选择文件并上传到当前业务记录;上传前确认文件版本、格式和归属。' }
return '按当前选中记录执行对应业务动作;根据弹窗提示核对后再确认。'
}
function Test-WritePage {
param([object]$Page)
if ($Page.writeProcedures.Length -gt 0) { return $true }
return [bool](@($Page.buttons | Where-Object { $_ -match '新增|新建|编辑|修改|删除|保存|确定|提交|开始|结束|完成|报工|送检|收料|发料|排产|下发|上传' }).Length)
}
$word = $null
$doc = $null
$buildError = $null
try {
$word = New-Object -ComObject Word.Application
$word.Visible = $false
$word.DisplayAlerts = 0
$doc = $word.Documents.Open($sourceDocx, $false, $false)
$doc.SaveAs2($docxPath, 16)
Write-BuildProgress '已打开系统管理篇并另存完整篇'
$selection = $word.Selection
$normal = $doc.Styles.Item(-1)
function Set-ParagraphText {
param([object]$Paragraph, [string]$Text)
$range = $Paragraph.Range.Duplicate
if ($range.End -gt $range.Start) { $range.End = $range.End - 1 }
$range.Text = $Text
}
$headingMap = @{
'7 典型业务链路' = '15 典型业务链路'
'7.1 新增可登录人员' = '15.1 新增可登录人员'
'7.2 下线一个角色' = '15.2 下线一个角色'
'7.3 新增生产工位' = '15.3 新增生产工位'
'7.4 追溯一次报工操作' = '15.4 追溯一次报工操作'
'8 常见问题' = '16 常见问题'
'9 正式环境操作红线' = '17 正式环境操作红线'
}
$paragraphCount = $doc.Paragraphs.Count
for ($paragraphIndex = 1; $paragraphIndex -le $paragraphCount; $paragraphIndex++) {
$paragraph = $doc.Paragraphs.Item($paragraphIndex)
$text = ($paragraph.Range.Text -replace '[\r\a]+$', '').Trim()
if ($text -eq '系统管理篇(样稿)') {
Set-ParagraphText $paragraph '完整操作手册(系统管理至销售订单)'
} elseif ($text -eq '版本V1.0') {
Set-ParagraphText $paragraph '版本V1.1'
} elseif ($text -eq '编制日期2026年7月23日') {
Set-ParagraphText $paragraph '编制日期2026年7月23日'
} elseif ($text.StartsWith('当前正式菜单以数据库配置为准,包括人员管理')) {
Set-ParagraphText $paragraph '当前正式菜单以数据库配置为准。本版在系统管理基础上继续覆盖设备管理、研发管理、工艺管理、计划排产、生产管理、异常提醒、质量管理和销售订单管理共计74个正式功能页面。'
} elseif ($headingMap.ContainsKey($text)) {
Set-ParagraphText $paragraph $headingMap[$text]
}
}
Write-BuildProgress '已更新封面、阅读说明和后续章节编号'
$revisionTable = $null
for ($tableIndex = 1; $tableIndex -le $doc.Tables.Count; $tableIndex++) {
$candidate = $doc.Tables.Item($tableIndex)
if ($candidate.Columns.Count -ge 4) {
$firstCell = ($candidate.Cell(1, 1).Range.Text -replace '[\r\a]+$', '').Trim()
if ($firstCell -eq '版本') {
$revisionTable = $candidate
break
}
}
}
if ($null -ne $revisionTable) {
$newRow = $revisionTable.Rows.Add()
$newRow.Cells.Item(1).Range.Text = 'V1.1'
$newRow.Cells.Item(2).Range.Text = '2026-07-23'
$newRow.Cells.Item(3).Range.Text = '增加设备至销售订单八个模块'
$newRow.Cells.Item(4).Range.Text = '新增69个正式功能页、69张页面截图及完整数据关系说明'
}
Write-BuildProgress '已更新修订记录'
$insertAt = $null
$paragraphCount = $doc.Paragraphs.Count
for ($paragraphIndex = 1; $paragraphIndex -le $paragraphCount; $paragraphIndex++) {
$paragraph = $doc.Paragraphs.Item($paragraphIndex)
$text = ($paragraph.Range.Text -replace '[\r\a]+$', '').Trim()
if ($text -eq '15 典型业务链路') {
$insertAt = $paragraph.Range.Start
break
}
}
if ($null -eq $insertAt) { throw '未找到“15 典型业务链路”插入位置' }
$selection.SetRange($insertAt, $insertAt)
Write-BuildProgress '已定位模块章节插入位置'
function Move-End {
$selection.SetRange($selection.Range.End, $selection.Range.End)
}
function Add-Text {
param(
[string]$Text,
[double]$Size = 10.5,
[bool]$Bold = $false,
[int]$Align = 0,
[int]$Color = 0,
[double]$Before = 0,
[double]$After = 6,
[double]$FirstIndentCm = 0
)
$selection.Font.NameFarEast = '宋体'
$selection.Font.Name = 'Arial'
$selection.Font.Size = $Size
$selection.Font.Bold = [int]$Bold
$selection.Font.Color = $Color
$selection.ParagraphFormat.Alignment = $Align
$selection.ParagraphFormat.SpaceBefore = $Before
$selection.ParagraphFormat.SpaceAfter = $After
$selection.ParagraphFormat.FirstLineIndent = $word.CentimetersToPoints($FirstIndentCm)
$selection.TypeText($Text)
$selection.TypeParagraph()
}
function Add-Heading {
param([string]$Text, [int]$Level)
$styleId = switch ($Level) { 1 { -2 } 2 { -3 } default { -4 } }
$selection.Style = $doc.Styles.Item($styleId)
$selection.Font.NameFarEast = '微软雅黑'
$selection.Font.Name = 'Arial'
$selection.Font.Color = 0x8F4A31
$selection.Font.Bold = 1
$selection.ParagraphFormat.KeepWithNext = -1
$selection.TypeText($Text)
$selection.TypeParagraph()
$selection.Style = $normal
}
function Add-Table {
param([object[]]$Rows, [double[]]$WidthsCm = @())
if ($Rows.Count -eq 0) { return }
$range = $selection.Range
$table = $doc.Tables.Add($range, $Rows.Count, $Rows[0].Count)
$table.Borders.Enable = 1
$table.Range.Font.NameFarEast = '宋体'
$table.Range.Font.Name = 'Arial'
$table.Range.Font.Size = 8.5
$table.Range.ParagraphFormat.SpaceAfter = 0
for ($r = 0; $r -lt $Rows.Count; $r++) {
for ($c = 0; $c -lt $Rows[$r].Count; $c++) {
$table.Cell($r + 1, $c + 1).Range.Text = [string]$Rows[$r][$c]
$table.Cell($r + 1, $c + 1).VerticalAlignment = 1
if ($WidthsCm.Count -gt $c) {
$table.Cell($r + 1, $c + 1).Width = $word.CentimetersToPoints($WidthsCm[$c])
}
}
}
$table.Rows.Item(1).Range.Bold = 1
$table.Rows.Item(1).Shading.BackgroundPatternColor = 0xE6D8D0
$table.Rows.Item(1).Range.ParagraphFormat.Alignment = 1
$table.AllowAutoFit = -1
$selection.SetRange($table.Range.End, $table.Range.End)
$selection.TypeParagraph()
}
function Add-Note {
param([string]$Title, [string]$Text, [string]$Kind = 'info')
$range = $selection.Range
$table = $doc.Tables.Add($range, 1, 1)
$table.Borders.Enable = 1
$cell = $table.Cell(1, 1)
$cell.Range.Text = $Title + '' + $Text
$cell.Range.Font.NameFarEast = '微软雅黑'
$cell.Range.Font.Name = 'Arial'
$cell.Range.Font.Size = 9.5
$cell.Range.ParagraphFormat.SpaceAfter = 0
if ($Kind -eq 'warn') { $cell.Shading.BackgroundPatternColor = 0xD9F2FF }
elseif ($Kind -eq 'danger') { $cell.Shading.BackgroundPatternColor = 0xD9E2F3 }
else { $cell.Shading.BackgroundPatternColor = 0xF4F1EE }
$selection.SetRange($table.Range.End, $table.Range.End)
$selection.TypeParagraph()
}
function Add-Figure {
param([string]$Path, [string]$Caption, [double]$MaxWidthCm = 16.4)
$selection.ParagraphFormat.Alignment = 1
$shape = $selection.InlineShapes.AddPicture($Path, $false, $true, $selection.Range)
$shape.LockAspectRatio = -1
$maxWidth = $word.CentimetersToPoints($MaxWidthCm)
if ($shape.Width -gt $maxWidth) { $shape.Width = $maxWidth }
$selection.SetRange($shape.Range.End, $shape.Range.End)
$selection.TypeParagraph()
Add-Text $Caption 9 $false 1 0x777777 0 8 0
}
function Add-PageBreak {
$selection.InsertBreak(7)
}
Add-PageBreak
foreach ($moduleName in $moduleChapters.Keys) {
$chapter = $moduleChapters[$moduleName]
$modulePages = @($catalog | Where-Object { $_.module -eq $moduleName } | Sort-Object pageIndex)
if ($modulePages.Count -eq 0) { continue }
Write-BuildProgress "开始生成第$chapter章 $moduleName"
Add-Heading ("$chapter $moduleName") 1
Add-Text $modulePages[0].moduleRelation 10.5 $false 0 0 0 6 0.74
Add-Note '本章范围' "本章按正式菜单顺序说明$($modulePages.Count)个功能页。截图仅执行登录、页面导航和默认查询,没有点击任何业务写入按钮。" 'warn'
$moduleRows = [System.Collections.Generic.List[object]]::new()
$moduleRows.Add(@('序号', '功能页面', '主要用途'))
for ($i = 0; $i -lt $modulePages.Count; $i++) {
$moduleRows.Add(@(($i + 1).ToString(), $modulePages[$i].title, $modulePages[$i].purpose))
}
Add-Table $moduleRows.ToArray() @(1.2, 3.4, 10.8)
for ($pageIndex = 0; $pageIndex -lt $modulePages.Count; $pageIndex++) {
$page = $modulePages[$pageIndex]
$section = "$chapter.$($pageIndex + 1)"
Write-BuildProgress "生成 $section $($page.title)"
Add-PageBreak
Add-Heading ("$section $($page.title)") 2
Add-Text $page.purpose 10.5 $false 0 0 0 6 0.74
$writePage = Test-WritePage $page
Add-Table @(
@('项目', '说明'),
@('菜单路径', "$moduleName > $($page.title)"),
@('页面路由', $page.route),
@('数据性质', $(if ($writePage) { '查询为只读;业务按钮可能新增、修改或改变状态' } else { '以只读查询、汇总或导出为主' }))
) @(3.2, 12.2)
Add-Figure (Join-Path $screenshots $page.screenshot) ("$section-1 $moduleName - $($page.title)正式页面")
Add-Heading ("$section.1 搜索与过滤") 3
if ($page.searchFields.Length -gt 0) {
$filterRows = [System.Collections.Generic.List[object]]::new()
$filterRows.Add(@('条件', '控件形式', '使用说明'))
foreach ($field in $page.searchFields) {
$filterRows.Add(@($field, (Get-FilterType $field), (Get-FilterGuide $field)))
}
Add-Table $filterRows.ToArray() @(3.2, 4.2, 8.2)
Add-Note '组合规则' '多个条件同时填写时,由页面一并传入后台查询,通常取条件交集。若查询不到记录,先逐项清空状态、日期和编号条件,再重新查询;精确或包含匹配以对应页面后台过程为准。' 'info'
} else {
Add-Text '本页面没有独立的顶部搜索区,进入页面后按默认条件加载数据,或通过页签、树节点、当前工位/人员上下文切换数据范围。' 10.5 $false 0 0 0 6 0.74
}
if ($page.tabs.Length -gt 0) {
Add-Table @(
@('页面页签/视图', '切换说明'),
@((Join-Values $page.tabs 20), '切换页签会改变展示维度或数据范围;导出前确认当前页签。')
) @(5.2, 10.4)
}
Add-Heading ("$section.2 列表字段与页面结果") 3
if ($page.tableColumns.Length -gt 0) {
$columnRows = [System.Collections.Generic.List[object]]::new()
$columnRows.Add(@('分组', '字段/列'))
for ($columnIndex = 0; $columnIndex -lt $page.tableColumns.Length; $columnIndex += 8) {
$endIndex = [Math]::Min($columnIndex + 7, $page.tableColumns.Length - 1)
$group = @($page.tableColumns[$columnIndex..$endIndex])
$columnRows.Add(@("$($columnIndex + 1)-$($endIndex + 1)", ($group -join '、')))
}
Add-Table $columnRows.ToArray() @(3.2, 12.4)
Add-Text '列表用于展示当前筛选结果。存在横向滚动条时,应左右滚动核对全部状态、数量、时间和操作列;存在分页时,页码和每页条数只影响当前显示范围。' 10.5 $false 0 0 0 6 0.74
} else {
Add-Text '该页面主要使用自定义看板、卡片或复用组件展示结果,字段布局以截图及页面实时内容为准。' 10.5 $false 0 0 0 6 0.74
}
Add-Heading ("$section.3 功能按钮与操作") 3
if ($page.buttons.Length -gt 0) {
$buttonRows = [System.Collections.Generic.List[object]]::new()
$buttonRows.Add(@('按钮/命令', '操作说明'))
foreach ($button in $page.buttons) {
$buttonRows.Add(@($button, (Get-ActionGuide $button)))
}
Add-Table $buttonRows.ToArray() @(4.2, 11.4)
} else {
Add-Text '本页主要通过大尺寸现场操作按钮、图标按钮、表格行操作或复用组件执行命令。操作前必须先选择当前任务,并根据按钮文字和确认提示核对目标记录。' 10.5 $false 0 0 0 6 0.74
}
if ($writePage) {
Add-Note '正式操作注意' '新增、编辑、删除、开始、结束、排产、报工、送检、收料、上传等操作会写入业务数据或改变状态。本文档编制未实际执行这些按钮;正式使用时应按岗位权限和业务单据逐条确认。' 'danger'
}
Add-Heading ("$section.4 前后数据关系") 3
Add-Text $page.moduleRelation 10.5 $false 0 0 0 6 0.74
Add-Table @(
@('关系层次', '内容'),
@('页面输入', $(if ($page.searchFields.Length -gt 0) { Join-Values $page.searchFields 18 } else { '当前登录人员、角色、工位、默认状态或上游任务上下文' })),
@('只读查询过程/接口', (Join-Values $page.readProcedures 10)),
@('写入过程/接口', (Join-Values $page.writeProcedures 10)),
@('页面输出', $(if ($page.tableColumns.Length -gt 0) { Join-Values $page.tableColumns 16 } else { '自定义看板、统计结果、状态卡片或复用组件结果' }))
) @(4.2, 11.4)
Add-Note '技术核对说明' '过程名称来自当前前端源码和已引用接口文件,用于说明数据流向;动态过程名、通用审批接口及服务端二次调用可能不会全部显示在本表中。' 'info'
}
Write-BuildProgress "完成第$chapter章 $moduleName"
}
Write-BuildProgress '开始更新目录并导出PDF'
if ($doc.TablesOfContents.Count -gt 0) {
$doc.TablesOfContents.Item(1).Update()
}
$doc.Save()
Write-BuildProgress 'DOCX正文及目录已保存'
$doc.ExportAsFixedFormat($pdfPath, 17)
Write-BuildProgress 'PDF已导出'
# WPS 兼容层在大型文档导出后读取统计信息可能长时间阻塞,统计校验改由生成后的 Open XML/PDF 文件完成。
$doc.Close($false)
[void][Runtime.InteropServices.Marshal]::ReleaseComObject($doc)
$doc = $null
$word.Quit()
[void][Runtime.InteropServices.Marshal]::ReleaseComObject($word)
$word = $null
Copy-Item -LiteralPath $docxPath -Destination $desktopDocx -Force
Copy-Item -LiteralPath $pdfPath -Destination $desktopPdf -Force
Write-Output "DOCX=$docxPath"
Write-Output "PDF=$pdfPath"
Write-BuildProgress '完整操作手册生成完成'
} catch {
$buildError = $_
Write-BuildProgress ("生成失败:" + $_.Exception.Message)
} finally {
if ($doc) {
try { $doc.Close($false) } catch { }
}
if ($word) {
try { $word.Quit() } catch { }
}
if ($doc) { [void][Runtime.InteropServices.Marshal]::ReleaseComObject($doc) }
if ($word) { [void][Runtime.InteropServices.Marshal]::ReleaseComObject($word) }
[GC]::Collect()
[GC]::WaitForPendingFinalizers()
}
if ($buildError) { throw $buildError }