#!/bin/zsh
# 福建水务营收系统 - 分离文档导出工具
# 将每个设计文档分别导出为不同格式,支持独立的Mermaid图表处理
set -e
# 颜色定义
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m'
# 日志函数
log_info() {
echo -e "${BLUE}[INFO]${NC} $1"
}
log_success() {
echo -e "${GREEN}[SUCCESS]${NC} $1"
}
log_warning() {
echo -e "${YELLOW}[WARNING]${NC} $1"
}
log_error() {
echo -e "${RED}[ERROR]${NC} $1"
}
# 检查依赖
check_dependencies() {
log_info "检查依赖工具..."
# 检查 pandoc
if ! command -v pandoc &> /dev/null; then
log_error "pandoc 未安装,请运行 'brew install pandoc' 安装"
exit 1
fi
# 检查 node.js 和 mermaid-cli
if ! command -v node &> /dev/null; then
log_error "Node.js 未安装,请从 https://nodejs.org/ 下载安装"
exit 1
fi
# 安装或检查 mermaid-cli
if ! npx mmdc --version &> /dev/null && ! command -v mmdc &> /dev/null; then
log_info "安装 mermaid-cli..."
npm install @mermaid-js/mermaid-cli --save-dev
if [ $? -ne 0 ]; then
log_error "mermaid-cli 安装失败"
exit 1
fi
fi
log_success "所有依赖检查完成"
}
# 处理单个文档中的Mermaid图表
process_single_document_mermaid() {
local input_file="$1"
local output_file="$2"
local doc_name="$3"
local temp_dir="temp_mermaid_${doc_name}_$$"
log_info "处理文档 $doc_name 中的Mermaid图表..."
# 创建临时目录
mkdir -p "$temp_dir"
local diagram_count=0
local in_mermaid=false
local mermaid_content=""
local line_number=0
# 创建文档头部
cat > "$output_file" << EOF
---
title: "$doc_name"
author: "系统设计团队"
date: "2024年12月19日"
documentclass: article
geometry: margin=1in
fontsize: 11pt
mainfont: "PingFang SC"
CJKmainfont: "PingFang SC"
---
EOF
while IFS= read -r line; do
line_number=$((line_number + 1))
if [[ "$line" =~ ^\`\`\`mermaid ]]; then
in_mermaid=true
mermaid_content=""
continue
elif [[ "$line" =~ ^\`\`\`$ ]] && [ "$in_mermaid" = true ]; then
# 处理Mermaid图表
diagram_count=$((diagram_count + 1))
local mermaid_file="$temp_dir/diagram_$diagram_count.mmd"
local png_file="$temp_dir/diagram_$diagram_count.png"
# 保存Mermaid代码
echo "$mermaid_content" > "$mermaid_file"
# 转换为PNG
log_info "转换 $doc_name 图表 $diagram_count..."
# 使用多种方式尝试转换
local convert_success=false
# 方式1:使用npx
if npx mmdc -i "$mermaid_file" -o "$png_file" -w 2400 -H 1600 --backgroundColor white --theme default 2>/dev/null; then
convert_success=true
# 方式2:使用全局mmdc
elif command -v mmdc &> /dev/null && mmdc -i "$mermaid_file" -o "$png_file" -w 2400 -H 1600 --backgroundColor white --theme default 2>/dev/null; then
convert_success=true
fi
if [ "$convert_success" = true ] && [ -f "$png_file" ]; then
# 调用Python脚本调整图片尺寸(限制高度23公分)
if command -v python3 &> /dev/null; then
python3 "scripts/resize_image.py" "$png_file" --max-height-cm 23 2>/dev/null || {
log_warning "$doc_name 图表 $diagram_count 尺寸调整失败,继续使用原图"
}
else
log_warning "Python3 未安装,跳过图片尺寸调整"
fi
# 成功转换,仅添加图片引用
echo "" >> "$output_file"
echo "**图表 $diagram_count**" >> "$output_file"
echo "" >> "$output_file"
echo "" >> "$output_file"
echo "" >> "$output_file"
log_success "$doc_name 图表 $diagram_count 转换成功"
else
# 转换失败,保留原始代码
echo "" >> "$output_file"
echo '```mermaid' >> "$output_file"
echo "$mermaid_content" >> "$output_file"
echo '```' >> "$output_file"
echo "" >> "$output_file"
log_warning "$doc_name 图表 $diagram_count 转换失败,保留原始代码"
fi
in_mermaid=false
elif [ "$in_mermaid" = true ]; then
mermaid_content+="$line"$'\n'
else
echo "$line" >> "$output_file"
fi
done < "$input_file"
log_success "$doc_name 图表处理完成,共处理了 $diagram_count 个图表"
# 返回临时目录路径供后续使用
echo "$temp_dir"
}
# 创建增强的CSS样式
create_enhanced_css() {
local css_file="$1"
cat > "$css_file" << 'CSS_EOF'
/* 福建水务营收系统文档样式 */
@page {
margin: 2cm;
size: A4;
}
body {
font-family: "PingFang SC", "Microsoft YaHei", "SimSun", sans-serif;
font-size: 11pt;
line-height: 1.6;
color: #333;
max-width: none;
margin: 0;
padding: 0;
}
/* 标题样式 */
h1 {
font-size: 18pt;
font-weight: bold;
color: #1f4e79;
margin-top: 24pt;
margin-bottom: 12pt;
border-bottom: 2pt solid #1f4e79;
padding-bottom: 6pt;
page-break-after: avoid;
}
h2 {
font-size: 16pt;
font-weight: bold;
color: #2f5597;
margin-top: 18pt;
margin-bottom: 10pt;
border-bottom: 1pt solid #2f5597;
padding-bottom: 4pt;
page-break-after: avoid;
}
h3 {
font-size: 14pt;
font-weight: bold;
color: #365f91;
margin-top: 14pt;
margin-bottom: 8pt;
page-break-after: avoid;
}
h4 {
font-size: 12pt;
font-weight: bold;
color: #4472c4;
margin-top: 12pt;
margin-bottom: 6pt;
page-break-after: avoid;
}
h5 {
font-size: 11pt;
font-weight: bold;
color: #5b9bd5;
margin-top: 10pt;
margin-bottom: 5pt;
page-break-after: avoid;
}
h6 {
font-size: 10pt;
font-weight: bold;
color: #70ad47;
margin-top: 8pt;
margin-bottom: 4pt;
page-break-after: avoid;
}
/* 段落样式 */
p {
margin-top: 0;
margin-bottom: 8pt;
text-align: justify;
text-justify: inter-ideograph;
}
/* 表格样式(黑色边框) */
table {
border-collapse: collapse;
width: 100%;
margin: 12pt 0;
font-size: 10pt;
page-break-inside: avoid;
border: 1pt solid #000; /* 外边框黑色 */
}
th {
background-color: #4472c4;
color: white;
font-weight: bold;
padding: 8pt;
border: 1pt solid #000; /* 表头黑线 */
text-align: center;
}
td {
padding: 6pt 8pt;
border: 1pt solid #000; /* 单元格黑线 */
vertical-align: top;
}
tr:nth-child(even) {
background-color: #f2f2f2;
}
/* 代码样式 */
code {
font-family: "Courier New", "Monaco", monospace;
font-size: 9pt;
background-color: #f5f5f5;
padding: 2pt 4pt;
border-radius: 2pt;
border: 1pt solid #e1e1e1;
}
pre {
font-family: "Courier New", "Monaco", monospace;
font-size: 9pt;
background-color: #f8f8f8;
padding: 12pt;
border: 1pt solid #e1e1e1;
border-radius: 4pt;
overflow-x: auto;
margin: 12pt 0;
page-break-inside: avoid;
}
pre code {
background: none;
padding: 0;
border: none;
}
/* 列表样式 */
ul, ol {
margin: 8pt 0;
padding-left: 24pt;
}
li {
margin: 4pt 0;
}
/* 图片样式 */
img {
max-width: 100%;
height: auto;
display: block;
margin: 12pt auto;
border: 1pt solid #e1e1e1;
border-radius: 4pt;
page-break-inside: avoid;
}
/* 图表标题 */
img + p, p + img {
text-align: center;
font-weight: bold;
color: #4472c4;
font-size: 10pt;
margin: 6pt 0;
}
/* 分页符 */
.page-break {
page-break-before: always;
}
/* 避免孤行和寡行 */
p, li, dt, dd {
orphans: 2;
widows: 2;
}
/* 链接样式 */
a {
color: #0563c1;
text-decoration: underline;
}
a:visited {
color: #954f72;
}
/* 引用样式 */
blockquote {
margin: 12pt 0;
padding: 12pt;
background-color: #f9f9f9;
border-left: 4pt solid #4472c4;
font-style: italic;
}
/* 水平分隔线 */
hr {
border: none;
border-top: 1pt solid #d1d1d1;
margin: 18pt 0;
}
/* Details/Summary 样式 */
details {
margin: 8pt 0;
border: 1pt solid #e1e1e1;
border-radius: 4pt;
padding: 8pt;
}
summary {
font-weight: bold;
cursor: pointer;
color: #4472c4;
margin-bottom: 8pt;
}
/* 强调样式 */
strong, b {
font-weight: bold;
color: #1f4e79;
}
em, i {
font-style: italic;
color: #365f91;
}
/* 印刷样式优化 */
@media print {
body {
font-size: 10pt;
line-height: 1.4;
}
h1 { font-size: 16pt; }
h2 { font-size: 14pt; }
h3 { font-size: 12pt; }
h4 { font-size: 11pt; }
h5 { font-size: 10pt; }
h6 { font-size: 9pt; }
table { font-size: 9pt; }
code, pre { font-size: 8pt; }
/* 避免在不适当的地方分页 */
h1, h2, h3, h4, h5, h6 {
page-break-after: avoid;
}
table, pre, img {
page-break-inside: avoid;
}
}
CSS_EOF
log_success "CSS样式文件创建完成"
}
# 导出单个文档为Word格式
export_single_to_docx() {
local input_file="$1"
local output_file="$2"
local doc_title="$3"
local temp_dir="$4"
log_info "导出 $doc_title 为Word格式..."
# 创建输出目录
mkdir -p "$(dirname "$output_file")"
# 使用pandoc转换
pandoc "$input_file" \
-o "$output_file" \
--from markdown+smart \
--to docx \
--standalone \
--toc \
--toc-depth=4 \
--highlight-style=tango \
--reference-doc=templates/reference2.docx \
--resource-path=".:$temp_dir" \
--metadata title="$doc_title" \
--metadata author="系统设计团队" \
--metadata date="2024年12月19日" \
2>/dev/null || \
pandoc "$input_file" \
-o "$output_file" \
--from markdown+smart \
--to docx \
--standalone \
--toc \
--toc-depth=4 \
--resource-path=".:$temp_dir" \
--metadata title="$doc_title" \
--metadata author="系统设计团队" \
--metadata date="2024年12月19日"
if [ $? -eq 0 ]; then
log_success "$doc_title Word文档导出成功: $output_file"
else
log_error "$doc_title Word文档导出失败"
return 1
fi
}
# 导出单个文档为PDF格式
export_single_to_pdf() {
local input_file="$1"
local output_file="$2"
local doc_title="$3"
local css_file="$4"
local temp_dir="$5"
log_info "导出 $doc_title 为PDF格式..."
# 创建输出目录
mkdir -p "$(dirname "$output_file")"
# 使用pandoc转换为PDF
pandoc "$input_file" \
-o "$output_file" \
--from markdown+smart \
--to pdf \
--pdf-engine=wkhtmltopdf \
--css="$css_file" \
--standalone \
--toc \
--toc-depth=4 \
--number-sections \
--resource-path=".:$temp_dir" \
--metadata title="$doc_title" \
--metadata author="系统设计团队" \
--metadata date="2024年12月19日" \
--variable geometry:margin=2cm \
--variable fontsize:11pt \
--variable CJKmainfont:"PingFang SC" \
2>/dev/null || \
pandoc "$input_file" \
-o "$output_file" \
--from markdown+smart \
--to html5 \
--css="$css_file" \
--standalone \
--toc \
--toc-depth=4 \
--number-sections \
--resource-path=".:$temp_dir" \
--metadata title="$doc_title" \
--metadata author="系统设计团队" \
--metadata date="2024年12月19日" | \
wkhtmltopdf --page-size A4 --margin-top 2cm --margin-bottom 2cm --margin-left 2cm --margin-right 2cm - "$output_file" 2>/dev/null
if [ $? -eq 0 ]; then
log_success "$doc_title PDF文档导出成功: $output_file"
else
log_warning "$doc_title PDF文档导出失败,尝试HTML方式..."
# 如果PDF导出失败,导出HTML作为替代
export_single_to_html "$input_file" "${output_file%.pdf}.html" "$doc_title" "$css_file" "$temp_dir"
fi
}
# 导出单个文档为HTML格式
export_single_to_html() {
local input_file="$1"
local output_file="$2"
local doc_title="$3"
local css_file="$4"
local temp_dir="$5"
log_info "导出 $doc_title 为HTML格式..."
# 创建输出目录
mkdir -p "$(dirname "$output_file")"
# 使用pandoc转换为HTML
pandoc "$input_file" \
-o "$output_file" \
--from markdown+smart \
--to html5 \
--css="$css_file" \
--standalone \
--toc \
--toc-depth=4 \
--number-sections \
--highlight-style=tango \
--resource-path=".:$temp_dir" \
--metadata title="$doc_title" \
--metadata author="系统设计团队" \
--metadata date="2024年12月19日" \
--include-in-header=<(echo '')
if [ $? -eq 0 ]; then
log_success "$doc_title HTML文档导出成功: $output_file"
else
log_error "$doc_title HTML文档导出失败"
return 1
fi
}
# 处理单个文档
process_single_document() {
local doc_file="$1"
local export_format="$2"
local css_file="$3"
# 从文件名提取文档名称和中文标题
local doc_name=$(basename "$doc_file" .md)
local doc_title=""
case "$doc_name" in
"02_System_Architecture")
doc_title="福建水务营收系统-系统架构设计"
;;
"02_Module_Traceability_Index")
doc_title="福建水务营收系统-模块追溯索引"
;;
"01_Database_Design")
doc_title="福建水务营收系统-数据库设计"
;;
"03_Interface_Design")
doc_title="福建水务营收系统-接口设计"
;;
"05_Deployment_Design")
doc_title="福建水务营收系统-部署设计"
;;
"04_Security_Design")
doc_title="福建水务营收系统-安全设计"
;;
"01_System_Overview")
doc_title="福建水务营收系统-系统概述"
;;
"03_Summary_Design")
doc_title="福建水务营收系统-概要设计说明书"
;;
"01_Detailed_Design")
doc_title="福建水务营收系统-详细设计说明书"
;;
"02_Table_Specs")
doc_title="福建水务营收系统-单表规格补充(历史映射)"
;;
"06_Sensitive_Data_Encryption")
doc_title="福建水务营收系统-敏感数据加密方案"
;;
"03_CA_Esignature_Supplement")
doc_title="福建水务营收系统-报装CA电子签章补充"
;;
"11_UP_Detailed")
doc_title="福建水务营收系统-统一平台模块正文"
;;
"12_REV_Detailed")
doc_title="福建水务营收系统-营收业务模块正文"
;;
"13_CS_Detailed")
doc_title="福建水务营收系统-客户服务模块正文"
;;
"14_METER_Detailed")
doc_title="福建水务营收系统-表务模块正文"
;;
"15_INST_Detailed")
doc_title="福建水务营收系统-报装与签章模块正文"
;;
*)
doc_title="福建水务营收系统-$doc_name"
;;
esac
log_info "处理文档: $doc_title"
# 处理Mermaid图表
local processed_md="output/${doc_name}_processed.md"
local temp_dir=$(process_single_document_mermaid "$doc_file" "$processed_md" "$doc_name")
# 导出不同格式
if [[ "$export_format" == "all" || "$export_format" == "docx" ]]; then
export_single_to_docx "$processed_md" "output/${doc_name}.docx" "$doc_title" "$temp_dir"
fi
if [[ "$export_format" == "all" || "$export_format" == "pdf" ]]; then
export_single_to_pdf "$processed_md" "output/${doc_name}.pdf" "$doc_title" "$css_file" "$temp_dir"
fi
if [[ "$export_format" == "all" || "$export_format" == "html" ]]; then
export_single_to_html "$processed_md" "output/${doc_name}.html" "$doc_title" "$css_file" "$temp_dir"
fi
log_success "文档 $doc_title 处理完成"
echo ""
}
# 主函数
main() {
local export_format="${1:-all}"
echo "🚀 福建水务营收系统 - 分离文档导出工具"
echo "========================================="
echo ""
# 检查依赖
check_dependencies
# 创建输出目录
mkdir -p output
# 创建CSS样式
local css_file="output/document_style.css"
create_enhanced_css "$css_file"
# 定义要处理的文档列表
local documents=(
"docs/design/01_Overview/01_System_Overview.md"
"docs/design/01_Overview/02_System_Architecture.md"
"docs/design/01_Overview/03_Summary_Design.md"
"docs/design/02_Detailed_Design/01_Detailed_Design.md"
"docs/design/02_Detailed_Design/02_Module_Traceability_Index.md"
"docs/design/02_Detailed_Design/03_CA_Esignature_Supplement.md"
"docs/design/02_Detailed_Design/11_UP_Detailed.md"
"docs/design/02_Detailed_Design/12_REV_Detailed.md"
"docs/design/02_Detailed_Design/13_CS_Detailed.md"
"docs/design/02_Detailed_Design/14_METER_Detailed.md"
"docs/design/02_Detailed_Design/15_INST_Detailed.md"
"docs/design/03_Technical_Design/01_Database_Design.md"
"docs/design/03_Technical_Design/02_Table_Specs.md"
"docs/design/03_Technical_Design/03_Interface_Design.md"
"docs/design/03_Technical_Design/04_Security_Design.md"
"docs/design/03_Technical_Design/05_Deployment_Design.md"
"docs/design/03_Technical_Design/06_Sensitive_Data_Encryption.md"
)
local processed_count=0
# 处理每个文档
for doc in "${documents[@]}"; do
if [[ -f "$doc" ]]; then
processed_count=$((processed_count + 1))
log_info "正在处理文档 [$processed_count/${#documents[@]}]: $doc"
process_single_document "$doc" "$export_format" "$css_file"
else
log_warning "文档不存在: $doc"
fi
done
echo ""
echo "🎉 分离文档导出完成!"
echo ""
echo "📁 输出文件 ($processed_count 个文档):"
for doc in "${documents[@]}"; do
if [[ -f "$doc" ]]; then
local doc_name=$(basename "$doc" .md)
if [[ "$export_format" == "all" || "$export_format" == "docx" ]]; then
echo " 📄 $doc_name.docx"
fi
if [[ "$export_format" == "all" || "$export_format" == "pdf" ]]; then
echo " 📄 $doc_name.pdf"
fi
if [[ "$export_format" == "all" || "$export_format" == "html" ]]; then
echo " 📄 $doc_name.html"
fi
fi
done
echo ""
echo "💡 提示:"
echo " - 每个文档都独立处理Mermaid图表"
echo " - 文档标题和内容保持独立"
echo " - 每个文档都有独立的目录结构"
echo " - 图表文件保存在对应的临时目录中"
echo ""
}
# 显示使用说明
show_usage() {
echo "用法: $0 [格式]"
echo ""
echo "格式选项:"
echo " all 导出所有格式 (默认)"
echo " docx 仅导出Word格式"
echo " pdf 仅导出PDF格式"
echo " html 仅导出HTML格式"
echo ""
echo "示例:"
echo " $0 # 导出所有格式"
echo " $0 docx # 仅导出Word"
echo " $0 pdf # 仅导出PDF"
echo " $0 html # 仅导出HTML"
}
# 处理命令行参数
case "${1:-}" in
-h|--help|help)
show_usage
exit 0
;;
all|docx|pdf|html|"")
main "${1:-all}"
;;
*)
echo "错误: 不支持的格式 '$1'"
echo ""
show_usage
exit 1
;;
esac