fujian_water_biz_doc/scripts/unified_export.sh

876 lines
22 KiB
Bash
Executable File
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#!/bin/zsh
# 福建水务营收系统 - 分离文档导出工具
# 将每个设计文档分别导出为不同格式支持独立的Mermaid图表处理
set -e
# 颜色定义
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
PURPLE='\033[0;35m'
NC='\033[0m'
# 全局变量
TEMP_DIRS=()
TEMP_FILES=()
# 日志函数
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"
}
log_cleanup() {
echo -e "${PURPLE}[CLEANUP]${NC} $1"
}
# 清理函数
cleanup_temp_files() {
log_cleanup "开始清理临时文件..."
local cleaned_dirs=0
local cleaned_files=0
# 清理临时目录
for temp_dir in "${TEMP_DIRS[@]}"; do
if [[ -d "$temp_dir" ]]; then
log_cleanup "删除临时目录: $temp_dir"
rm -rf "$temp_dir"
cleaned_dirs=$((cleaned_dirs + 1))
fi
done
# 清理临时文件
for temp_file in "${TEMP_FILES[@]}"; do
if [[ -f "$temp_file" ]]; then
log_cleanup "删除临时文件: $temp_file"
rm -f "$temp_file"
cleaned_files=$((cleaned_files + 1))
fi
done
# 清理通用临时文件模式
find . -name "temp_mermaid_*" -type d -exec rm -rf {} + 2>/dev/null || true
find . -name "*_processed.md" -type f -exec rm -f {} + 2>/dev/null || true
find . -name "*.tmp" -type f -exec rm -f {} + 2>/dev/null || true
log_success "临时文件清理完成 (目录: $cleaned_dirs, 文件: $cleaned_files)"
}
# 清理输出缓存
cleanup_output_cache() {
log_cleanup "开始清理输出缓存..."
local cleaned_count=0
if [[ -d "output" ]]; then
# 清理处理过的markdown文件
find output -name "*_processed.md" -type f -exec rm -f {} + 2>/dev/null || true
# 清理旧的图表文件
find output -name "diagram_*.png" -type f -exec rm -f {} + 2>/dev/null || true
find output -name "*.mmd" -type f -exec rm -f {} + 2>/dev/null || true
# 统计清理的文件数量
cleaned_count=$(find output -name "*_processed.md" -o -name "diagram_*.png" -o -name "*.mmd" 2>/dev/null | wc -l)
log_success "输出缓存清理完成 (清理文件: $cleaned_count)"
else
log_info "输出目录不存在,无需清理缓存"
fi
}
# 全面清理
cleanup_all() {
log_cleanup "开始全面清理..."
# 清理临时文件
cleanup_temp_files
# 清理输出缓存
cleanup_output_cache
# 清理所有输出文件
if [[ -d "output" ]]; then
log_cleanup "清理所有输出文件..."
rm -rf output/*
log_success "所有输出文件已清理"
fi
# 清理node_modules中的临时文件
if [[ -d "node_modules" ]]; then
find node_modules -name "*.tmp" -type f -exec rm -f {} + 2>/dev/null || true
fi
log_success "全面清理完成"
}
# 设置清理陷阱
setup_cleanup_trap() {
# 在脚本退出时自动清理
trap 'cleanup_temp_files' EXIT
# 在收到中断信号时清理
trap 'log_warning "收到中断信号,正在清理..."; cleanup_temp_files; exit 1' INT TERM
}
# 检查依赖
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"
TEMP_DIRS+=("$temp_dir")
# 记录输出文件
TEMP_FILES+=("$output_file")
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"
# 记录临时文件
TEMP_FILES+=("$mermaid_file" "$png_file")
# 保存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 1200 -H 800 --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 1200 -H 800 --backgroundColor white --theme default 2>/dev/null; then
convert_success=true
fi
if [ "$convert_success" = true ] && [ -f "$png_file" ]; then
# 成功转换,仅添加图片引用
echo "" >> "$output_file"
echo "**图表 $diagram_count**" >> "$output_file"
echo "" >> "$output_file"
echo "![图表 $diagram_count]($png_file)" >> "$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;
}
th {
background-color: #4472c4;
color: white;
font-weight: bold;
padding: 8pt;
border: 1pt solid #2f5597;
text-align: center;
}
td {
padding: 6pt 8pt;
border: 1pt solid #a6a6a6;
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/reference.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 '<meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0">')
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
"water_biz_system_architecture")
doc_title="福建水务营收系统-系统架构设计"
;;
"water_biz_module_design")
doc_title="福建水务营收系统-模块功能设计"
;;
"water_biz_database_design")
doc_title="福建水务营收系统-数据库设计"
;;
"water_biz_interface_design")
doc_title="福建水务营收系统-接口设计"
;;
"water_biz_deployment_design")
doc_title="福建水务营收系统-部署设计"
;;
"water_biz_security_design")
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 ""
# 设置清理陷阱
setup_cleanup_trap
# 开始前清理旧的临时文件
log_cleanup "清理旧的临时文件和缓存..."
cleanup_temp_files
cleanup_output_cache
# 检查依赖
check_dependencies
# 创建输出目录
mkdir -p output
# 创建CSS样式
local css_file="output/document_style.css"
create_enhanced_css "$css_file"
# 定义要处理的文档列表
local documents=(
"water_biz_system_architecture.md"
"water_biz_module_design.md"
"water_biz_database_design.md"
"water_biz_interface_design.md"
"water_biz_deployment_design.md"
"water_biz_security_design.md"
"water_biz_overview_design.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"
# 处理完每个文档后进行部分清理
log_cleanup "清理文档 $doc 的临时文件..."
local doc_name=$(basename "$doc" .md)
find . -name "temp_mermaid_${doc_name}_*" -type d -exec rm -rf {} + 2>/dev/null || true
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 " - 临时文件已自动清理"
echo ""
# 最终清理检查
log_cleanup "执行最终清理检查..."
cleanup_temp_files
}
# 显示使用说明
show_usage() {
echo "用法: $0 [选项]"
echo ""
echo "格式选项:"
echo " all 导出所有格式 (默认)"
echo " docx 仅导出Word格式"
echo " pdf 仅导出PDF格式"
echo " html 仅导出HTML格式"
echo ""
echo "清理选项:"
echo " clean 清理临时文件和缓存"
echo " clean-all 清理所有文件包括输出"
echo " clean-cache 仅清理缓存文件"
echo ""
echo "示例:"
echo " $0 # 导出所有格式"
echo " $0 docx # 仅导出Word"
echo " $0 pdf # 仅导出PDF"
echo " $0 html # 仅导出HTML"
echo " $0 clean # 清理临时文件"
echo " $0 clean-all # 清理所有文件"
echo " $0 clean-cache # 清理缓存"
}
# 处理命令行参数
case "${1:-}" in
-h|--help|help)
show_usage
exit 0
;;
clean)
echo "🧹 开始清理临时文件和缓存..."
cleanup_temp_files
cleanup_output_cache
echo "✅ 清理完成"
exit 0
;;
clean-all)
echo "🧹 开始全面清理..."
cleanup_all
echo "✅ 全面清理完成"
exit 0
;;
clean-cache)
echo "🧹 开始清理缓存..."
cleanup_output_cache
echo "✅ 缓存清理完成"
exit 0
;;
all|docx|pdf|html|"")
main "${1:-all}"
;;
*)
echo "错误: 不支持的选项 '$1'"
echo ""
show_usage
exit 1
;;
esac