# -*- coding: utf-8 -*-
from docx import Document
from docx.shared import Pt, Cm, RGBColor, Inches
from docx.enum.text import WD_ALIGN_PARAGRAPH, WD_TAB_LEADER
from docx.enum.table import WD_TABLE_ALIGNMENT, WD_ALIGN_VERTICAL
from docx.oxml.ns import qn
from docx.oxml import OxmlElement
import copy

doc = Document()

# ── 页面设置 A4 ──────────────────────────────────────────────
section = doc.sections[0]
section.page_width  = Cm(21)
section.page_height = Cm(29.7)
section.left_margin   = Cm(2.5)
section.right_margin  = Cm(2.5)
section.top_margin    = Cm(2.5)
section.bottom_margin = Cm(2.0)

# ── 颜色常量 ────────────────────────────────────────────────
C_TITLE  = RGBColor(0x1e, 0x3a, 0x8a)   # 深蓝
C_H2     = RGBColor(0x1d, 0x4e, 0xd8)   # 中蓝
C_H3     = RGBColor(0x0e, 0x7a, 0x9e)   # 青蓝
C_LABEL  = RGBColor(0x37, 0x41, 0x51)   # 深灰
C_BODY   = RGBColor(0x1f, 0x29, 0x37)   # 正文黑
C_WHITE  = RGBColor(0xFF, 0xFF, 0xFF)
C_TH_BG  = RGBColor(0x1e, 0x40, 0xaf)
C_ROW1   = RGBColor(0xef, 0xf6, 0xff)
C_ROW2   = RGBColor(0xff, 0xff, 0xff)
C_BORDER = RGBColor(0xc3, 0xda, 0xf7)
C_NOTE_BG= RGBColor(0xfe, 0xf9, 0xc3)
C_NOTE_BD= RGBColor(0xf5, 0x9e, 0x0b)

def set_cell_bg(cell, rgb: RGBColor):
    tc = cell._tc
    tcPr = tc.get_or_add_tcPr()
    shd = OxmlElement('w:shd')
    shd.set(qn('w:val'), 'clear')
    shd.set(qn('w:color'), 'auto')
    hex_color = '{:02X}{:02X}{:02X}'.format(rgb[0], rgb[1], rgb[2])
    shd.set(qn('w:fill'), hex_color)
    tcPr.append(shd)

def set_cell_border(cell, top=None, bottom=None, left=None, right=None):
    tc = cell._tc
    tcPr = tc.get_or_add_tcPr()
    tcBorders = OxmlElement('w:tcBorders')
    for side, val in [('top',top),('bottom',bottom),('left',left),('right',right)]:
        if val:
            el = OxmlElement(f'w:{side}')
            el.set(qn('w:val'), val.get('val','single'))
            el.set(qn('w:sz'), str(val.get('sz',4)))
            el.set(qn('w:space'), '0')
            el.set(qn('w:color'), val.get('color','C3DAF7'))
            tcBorders.append(el)
    tcPr.append(tcBorders)

def add_run(para, text, bold=False, size=None, color=None, italic=False):
    run = para.add_run(text)
    run.bold = bold
    run.italic = italic
    run.font.name = '微软雅黑'
    run._element.rPr.rFonts.set(qn('w:eastAsia'), '微软雅黑')
    if size:
        run.font.size = Pt(size)
    if color:
        run.font.color.rgb = color
    return run

def heading1(text):
    para = doc.add_paragraph()
    para.paragraph_format.space_before = Pt(18)
    para.paragraph_format.space_after  = Pt(8)
    para.paragraph_format.left_indent  = Cm(0)
    # 左侧蓝条效果通过 border
    pPr = para._p.get_or_add_pPr()
    pBdr = OxmlElement('w:pBdr')
    left = OxmlElement('w:left')
    left.set(qn('w:val'), 'single')
    left.set(qn('w:sz'), '24')
    left.set(qn('w:space'), '6')
    left.set(qn('w:color'), '1E3A8A')
    pBdr.append(left)
    pPr.append(pBdr)
    para.paragraph_format.left_indent = Cm(0.5)
    add_run(para, text, bold=True, size=14, color=C_TITLE)
    return para

def heading2(text):
    para = doc.add_paragraph()
    para.paragraph_format.space_before = Pt(10)
    para.paragraph_format.space_after  = Pt(4)
    add_run(para, '◆ ', bold=True, size=12, color=C_H2)
    add_run(para, text, bold=True, size=12, color=C_H2)
    return para

def heading3(text):
    para = doc.add_paragraph()
    para.paragraph_format.space_before = Pt(6)
    para.paragraph_format.space_after  = Pt(3)
    add_run(para, '▸ ', bold=True, size=11, color=C_H3)
    add_run(para, text, bold=True, size=11, color=C_H3)
    return para

def body(text, indent=0):
    para = doc.add_paragraph()
    para.paragraph_format.space_before = Pt(2)
    para.paragraph_format.space_after  = Pt(2)
    para.paragraph_format.left_indent  = Cm(indent)
    para.paragraph_format.first_line_indent = Cm(0)
    add_run(para, text, size=10.5, color=C_BODY)
    return para

def bullet(text, level=0):
    para = doc.add_paragraph()
    para.paragraph_format.space_before = Pt(1)
    para.paragraph_format.space_after  = Pt(1)
    indent = 0.5 + level * 0.5
    para.paragraph_format.left_indent = Cm(indent)
    bullets = ['●', '○', '–']
    add_run(para, bullets[level] + '  ', bold=(level==0), size=10, color=C_H3)
    add_run(para, text, size=10, color=C_BODY)
    return para

def note(text):
    para = doc.add_paragraph()
    para.paragraph_format.space_before = Pt(6)
    para.paragraph_format.space_after  = Pt(6)
    para.paragraph_format.left_indent  = Cm(0.5)
    para.paragraph_format.right_indent = Cm(0.5)
    pPr = para._p.get_or_add_pPr()
    pBdr = OxmlElement('w:pBdr')
    for side in ['top','bottom','left','right']:
        el = OxmlElement(f'w:{side}')
        el.set(qn('w:val'), 'single')
        el.set(qn('w:sz'), '4' if side in ('top','bottom','right') else '18')
        el.set(qn('w:space'), '4')
        el.set(qn('w:color'), 'F59E0B')
        pBdr.append(el)
    pPr.append(pBdr)
    add_run(para, '📌 注意：', bold=True, size=10, color=RGBColor(0xd9,0x77,0x06))
    add_run(para, text, size=10, color=C_BODY)
    return para

def make_table(headers, rows, col_widths=None):
    table = doc.add_table(rows=1+len(rows), cols=len(headers))
    table.alignment = WD_TABLE_ALIGNMENT.CENTER
    table.style = 'Table Grid'
    # 表头
    hdr = table.rows[0]
    for i, h in enumerate(headers):
        cell = hdr.cells[i]
        set_cell_bg(cell, C_TH_BG)
        cell.vertical_alignment = WD_ALIGN_VERTICAL.CENTER
        para = cell.paragraphs[0]
        para.alignment = WD_ALIGN_PARAGRAPH.CENTER
        add_run(para, h, bold=True, size=10, color=C_WHITE)
    # 数据行
    for r_idx, row in enumerate(rows):
        tr = table.rows[r_idx+1]
        bg = C_ROW1 if r_idx % 2 == 0 else C_ROW2
        for c_idx, val in enumerate(row):
            cell = tr.cells[c_idx]
            set_cell_bg(cell, bg)
            cell.vertical_alignment = WD_ALIGN_VERTICAL.CENTER
            para = cell.paragraphs[0]
            para.alignment = WD_ALIGN_PARAGRAPH.LEFT
            add_run(para, str(val), size=10, color=C_BODY)
    # 列宽
    if col_widths:
        for i, w in enumerate(col_widths):
            for row in table.rows:
                row.cells[i].width = Cm(w)
    doc.add_paragraph()  # 表格后空行
    return table

def divider():
    para = doc.add_paragraph()
    para.paragraph_format.space_before = Pt(4)
    para.paragraph_format.space_after  = Pt(4)
    pPr = para._p.get_or_add_pPr()
    pBdr = OxmlElement('w:pBdr')
    bottom = OxmlElement('w:bottom')
    bottom.set(qn('w:val'), 'single')
    bottom.set(qn('w:sz'), '6')
    bottom.set(qn('w:space'), '1')
    bottom.set(qn('w:color'), 'C3DAF7')
    pBdr.append(bottom)
    pPr.append(pBdr)

# ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
# 封面
# ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
doc.add_paragraph()
doc.add_paragraph()
doc.add_paragraph()

p = doc.add_paragraph()
p.alignment = WD_ALIGN_PARAGRAPH.CENTER
add_run(p, '实验室安全智能监测与管控中心', bold=True, size=22, color=C_TITLE)

doc.add_paragraph()
p = doc.add_paragraph()
p.alignment = WD_ALIGN_PARAGRAPH.CENTER
add_run(p, '数据可视化大屏', bold=True, size=16, color=C_H2)

doc.add_paragraph()
p = doc.add_paragraph()
p.alignment = WD_ALIGN_PARAGRAPH.CENTER
add_run(p, '使  用  说  明  文  档', bold=True, size=14, color=C_H3)

doc.add_paragraph()
doc.add_paragraph()

p = doc.add_paragraph()
p.alignment = WD_ALIGN_PARAGRAPH.CENTER
add_run(p, '中国安全生产科学研究院', bold=True, size=12, color=C_LABEL)

p = doc.add_paragraph()
p.alignment = WD_ALIGN_PARAGRAPH.CENTER
add_run(p, 'National Institute for Occupational Safety', italic=True, size=10, color=C_LABEL)

doc.add_paragraph()
p = doc.add_paragraph()
p.alignment = WD_ALIGN_PARAGRAPH.CENTER
add_run(p, '文档版本：V1.0　　编制日期：2026年03月', size=10, color=C_LABEL)

doc.add_page_break()

# ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
# 目录
# ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
p = doc.add_paragraph()
p.alignment = WD_ALIGN_PARAGRAPH.CENTER
add_run(p, '目　　录', bold=True, size=14, color=C_TITLE)
doc.add_paragraph()

toc_items = [
    ('1', '文档概述', '3'),
    ('2', '系统简介', '3'),
    ('3', '运行环境与访问方式', '4'),
    ('4', '整体界面布局说明', '4'),
    ('5', '顶部导航栏', '5'),
    ('6', '1区 — 实验室基本情况统计', '5'),
    ('7', '2区 — 智能设备与实验室设备统计', '7'),
    ('8', '3区 — 实时监控', '8'),
    ('9', '4区 — 实验环境感知与风险预警', '10'),
    ('10', '全屏预警弹窗', '11'),
    ('11', '通用交互说明', '12'),
    ('12', '数据说明', '12'),
    ('13', '常见问题（FAQ）', '13'),
]
for no, title, page in toc_items:
    para = doc.add_paragraph()
    para.paragraph_format.space_before = Pt(3)
    para.paragraph_format.space_after  = Pt(3)
    tab_stops = para.paragraph_format.tab_stops
    tab_stops.add_tab_stop(Cm(14.5), leader=WD_TAB_LEADER.DOTS)
    add_run(para, f'{no}. {title}', size=10.5, color=C_BODY)
    para.add_run('\t')
    add_run(para, page, size=10.5, color=C_BODY)

doc.add_page_break()

# ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
# 1. 文档概述
# ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
heading1('1. 文档概述')
body('本文档为《实验室安全智能监测与管控中心》数据可视化大屏的使用说明，旨在帮助操作人员、管理人员及相关使用者快速了解系统各功能模块的展示内容、交互方式及数据含义，确保系统功能得到正确、高效的使用。')
doc.add_paragraph()
body('本文档适用对象：')
bullet('实验室安全管理人员（日常监控运维）')
bullet('院级行政管理人员（决策数据查看）')
bullet('IT运维人员（系统部署与维护参考）')

divider()

# ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
# 2. 系统简介
# ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
heading1('2. 系统简介')
body('《实验室安全智能监测与管控中心》大屏系统是面向中国安全生产科学研究院（简称"安科院"）各实验室的综合安全管理可视化平台，通过数据可视化手段将实验室的安全状态、环境感知数据、设备运行状况、人员进出信息及风险预警等核心数据以直观、实时的方式呈现于大屏上，便于管理人员进行统一监控与快速决策。')

doc.add_paragraph()
heading2('系统定位')
make_table(
    ['维度', '说明'],
    [
        ['系统名称', '实验室安全智能监测与管控中心'],
        ['所属单位', '中国安全生产科学研究院（National Institute for Occupational Safety）'],
        ['系统类型', '数据可视化大屏（单页面应用）'],
        ['设计分辨率', '9600 × 2800 px（约为1920×1080的2.5倍）'],
        ['视觉风格', '深蓝科幻风，边框流动效果，科技感动效图标'],
        ['技术框架', 'HTML5 + CSS3 + ECharts 5.4.3（单文件内联，无需构建工具）'],
    ],
    col_widths=[4, 11.5]
)

heading2('主要功能概览')
bullet('实验室基本情况统计（总数、分级、状态）')
bullet('实验室安全分级统计（各二级单位堆叠柱状图）')
bullet('实验室人员进出数量统计及走势折线图')
bullet('智能环境感知应用设备统计（在线率、设备分类）')
bullet('实验室设备分类及使用统计（分类、使用率、状态）')
bullet('3区固定显示实时监控（9宫格监控画面，AI危险行为检测）')
bullet('实验环境安全智能感知（传感器实时数据滚动）')
bullet('实验室实时风险预警（预警通知轮播）')
bullet('全屏预警弹窗（超阈值自动触发，科幻警报样式）')

divider()

# ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
# 3. 运行环境与访问方式
# ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
heading1('3. 运行环境与访问方式')
heading2('硬件环境要求')
make_table(
    ['项目', '最低要求', '推荐配置'],
    [
        ['显示屏', '1920×1080（FHD）', '超大屏/拼接屏 9600×2800 px'],
        ['处理器', 'Intel i5 / 同等级别', 'Intel i7 或以上'],
        ['内存', '8 GB', '16 GB 或以上'],
        ['显卡', '支持硬件加速', '独立显卡（GPU加速渲染）'],
    ],
    col_widths=[4, 5, 6.5]
)

heading2('软件环境要求')
make_table(
    ['软件', '版本要求', '说明'],
    [
        ['浏览器', 'Chrome 90+ / Edge 90+', '推荐使用最新版 Chrome，需支持 WebGL'],
        ['ECharts CDN', '5.4.3', '通过 jsDelivr CDN 在线加载，需保证网络可达'],
        ['操作系统', 'Windows 10+ / macOS 12+', '支持主流64位操作系统'],
    ],
    col_widths=[3, 5, 7.5]
)

heading2('访问方式')
bullet('将项目文件夹（含 index-v2.html 及 baseBg.png）部署到本地或局域网 Web 服务器。')
bullet('用浏览器打开 http://[服务器IP]/index-v2.html 即可访问。')
bullet('也可直接双击 index-v2.html 在浏览器中本地打开（部分资源需本地服务器访问）。')
bullet('首次加载时，系统将自动请求进入全屏模式（浏览器弹出授权提示，点击"允许"即可）。')

note('若 ECharts 图表未正常显示，请检查网络连接是否可访问 cdn.jsdelivr.net，或将 ECharts 替换为本地文件引用。')
divider()

# ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
# 4. 整体界面布局说明
# ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
heading1('4. 整体界面布局说明')
body('大屏整体由顶部导航栏 + 内容区域两大部分构成，内容区域按从左到右 2:1.5:4.5:2 的比例划分为四个区域。布局示意如下：')
doc.add_paragraph()

# 布局示意表
table = doc.add_table(rows=3, cols=5)
table.alignment = WD_TABLE_ALIGNMENT.CENTER
table.style = 'Table Grid'

# 第1行：导航栏
row0 = table.rows[0]
row0.cells[0].merge(row0.cells[4])
set_cell_bg(row0.cells[0], C_TH_BG)
p = row0.cells[0].paragraphs[0]
p.alignment = WD_ALIGN_PARAGRAPH.CENTER
add_run(p, '顶部导航栏（时钟 | LOGO + 单位名称 + 系统大标题 | 天气）', bold=True, size=10, color=C_WHITE)

# 第2行：列标题
row1 = table.rows[1]
for i, (txt, bg) in enumerate([
    ('1区（比例2）', C_ROW1),
    ('2区（比例1.5）', C_ROW2),
    ('3区（比例4.5）', C_ROW1),
    ('4区（比例2）', C_ROW2),
]):
    if i < 4:
        cell = row1.cells[i]
        set_cell_bg(cell, C_TH_BG if i in [0,2] else RGBColor(0x1d, 0x4e, 0xd8))
        p = cell.paragraphs[0]
        p.alignment = WD_ALIGN_PARAGRAPH.CENTER
        add_run(p, txt, bold=True, size=10, color=C_WHITE)
# merge last 2 cols for col 4
# Actually use 4 cols only: remove 5th
row1.cells[4].merge(row1.cells[3])

# 第3行：内容描述
row2 = table.rows[2]
contents = [
    '• 基本情况统计\n• 安全分级统计\n• 人员进出走势',
    '• 智能环境感知\n  应用设备统计\n• 设备分类及\n  使用统计',
    '实时监控（固定显示）\n左侧：搜索/筛选/树状图\n右侧：9宫格摄像头',
    '• 实验环境安全\n  智能感知\n• 实时风险预警',
]
bgs = [C_ROW1, C_ROW2, C_ROW1, C_ROW2]
for i, (txt, bg) in enumerate(zip(contents, bgs)):
    cell = row2.cells[i]
    set_cell_bg(cell, bg)
    cell.vertical_alignment = WD_ALIGN_VERTICAL.CENTER
    p = cell.paragraphs[0]
    p.alignment = WD_ALIGN_PARAGRAPH.LEFT
    add_run(p, txt, size=9.5, color=C_BODY)

row2.cells[4].merge(row2.cells[3])
doc.add_paragraph()

body('各区域均设有科幻装饰线框（四角装饰线）、边框流动动画（border-beam）及动效图标，模块之间有呼吸感的间距，整体采用深蓝科幻主题色系。')

divider()

# ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
# 5. 顶部导航栏
# ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
heading1('5. 顶部导航栏')
body('导航栏位于页面最顶部，横贯全宽，分为左、中、右三个区域。')

make_table(
    ['区域', '显示内容', '说明'],
    [
        ['左侧', '实时时钟（时:分:秒）\n日期及星期', '每秒自动更新，金色数字显示，字体采用等宽字体'],
        ['中间', 'LOGO图标\n中国安全生产科学研究院（单位名称）\n竖向分隔线\n实验室安全智能监测与管控中心（系统大标题）', 'LOGO为蓝色方形图标，带脉冲光晕动效；\n大标题采用渐变流光动画，中英文双语单位名称显示'],
        ['右侧', '城市 · 天气状况\n气温（°C） / AQI空气质量指数', '显示当前天气信息及空气质量'],
    ],
    col_widths=[2.5, 7, 6]
)

note('导航栏不含任何操作按钮，3区实时监控为固定显示，无需切换。')
divider()

# ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
# 6. 1区
# ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
heading1('6. 1区 — 实验室基本情况统计')
body('1区位于大屏左侧，由上至下包含三个功能模块。')

heading2('6.1 实验室基本情况统计')
body('本模块分为上部（80%）和下部（20%）两部分：')
doc.add_paragraph()
heading3('上部（按左4:右6比例划分）')
bullet('左侧（4）：SVG圆弧仪表图形，展示实验室总数（共128间），图形中心显示总数数字，下方配分级比例色条（I级红、II级橙、III级黄、IV级蓝）。', 0)
bullet('右侧（6）：环形图（donut chart），展示四个安全分级（I~IV级）的占比及数量，图外标注各级名称、数量（间）、百分比；图表圆心显示总数；右侧配彩色左边框明细行。', 0)

doc.add_paragraph()
heading3('下部（三种状态徽章）')
bullet('横向并排三个状态徽章，实时显示各状态实验室数量：', 0)
bullet('使用（绿色）：当前正在使用的实验室数量', 1)
bullet('异常（橙色）：当前处于异常告警状态的实验室数量', 1)
bullet('空闲（靛蓝）：当前闲置的实验室数量', 1)

doc.add_paragraph()
body('分级说明：')
make_table(
    ['安全级别', '标识颜色', '含义', '数量（示例）'],
    [
        ['I 级', '红色', '危险实验室', '12 间'],
        ['II 级', '橙色', '较危险实验室', '28 间'],
        ['III 级', '黄色', '一般危险实验室', '45 间'],
        ['IV 级', '蓝色', '较安全实验室', '43 间'],
    ],
    col_widths=[3, 3, 5, 4.5]
)

divider()
heading2('6.2 实验室安全分级统计')
body('本模块使用堆叠柱状图统计各二级单位实验室总数及分级数据。')
bullet('X轴：各二级单位名称，单位名称下方显示该单位实验室总数。')
bullet('Y轴：实验室数量（间）。')
bullet('每根柱子由I~IV级叠加组成，颜色对应分级（红/橙/黄/蓝）。')
bullet('图表顶部显示图例（I级、II级、III级、IV级及对应颜色）。')
bullet('一屏展示6个柱子，当数据量超出时，图表每5秒自动向左滚动1个单位，循环展示所有二级单位数据。')

doc.add_paragraph()
note('鼠标悬停在柱状图上可显示当前单位各分级详细数量的悬浮提示框。')
divider()

heading2('6.3 实验室进入人数统计及走势')
body('本模块展示今日实验室进入与在场人数统计，以及全天走势趋势。')
doc.add_paragraph()
heading3('数字翻牌器（上方）')
bullet('今日进入总人数：以翻牌动画形式显示，共4位数字滚动展示，金色高亮。')
bullet('当前在场实验人数：同样以翻牌动画形式显示，实时更新。')

heading3('折线图（下方）')
bullet('X轴：当天0~24小时，按9个时间节点显示（00:00、03:00、06:00…24:00）。')
bullet('Y轴：人数（人）。')
bullet('进入人数（蓝色实线+渐变填充）与在场人数（金色实线+渐变填充）双线对比展示。')

divider()

# ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
# 7. 2区
# ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
heading1('7. 2区 — 智能设备与实验室设备统计')
body('2区位于1区右侧，由上至下包含两个功能模块。')

heading2('7.1 智能环境感知应用设备统计')
body('本模块统计展示布设的智能物联应用设备整体情况，分为上部统计栏和下部图表区。')
doc.add_paragraph()
heading3('上部统计栏')
bullet('在线设备数量（绿色高亮，示例：312台）')
bullet('离线设备数量（红色高亮，示例：18台）')

heading3('下部图表区（左右布局）')
bullet('左侧：速度仪表盘样式图表，展示设备在线率百分比（示例：94.5%），金色数字显示。')
bullet('右侧：2×2网格卡片，分别显示：')
bullet('电子信息铭牌（86台）', 1)
bullet('化学品智能终端（44台）', 1)
bullet('传感器套件（128台）', 1)
bullet('智能摄像设备（72台）', 1)

divider()
heading2('7.2 实验室设备分类及使用统计')
body('本模块按 4:2:4 比例分为上、中、下三部分：')
doc.add_paragraph()
heading3('上部（占4）— 设备分类环形图')
bullet('使用环形图展示所有实验室设备的分类分布情况。')
bullet('右侧显示图例：各设备分类的颜色圆点标识、分类名称及数量（台）。')
body('设备分类示例：', indent=1)
make_table(
    ['分类', '数量（台）'],
    [
        ['检测设备', '680'],
        ['分析仪器', '520'],
        ['制备设备', '380'],
        ['安全设备', '280'],
        ['辅助设备', '240'],
        ['其他', '358'],
    ],
    col_widths=[7, 8.5]
)

heading3('中部（占2）— 汇总统计')
make_table(
    ['指标', '说明'],
    [
        ['设备总数', '所有实验室设备总数（示例：2,458台）'],
        ['使用总时长', '累计设备使用时长（示例：18,620小时）'],
        ['设备使用率', '当前设备使用率（示例：62.4%）'],
    ],
    col_widths=[4, 11.5]
)

heading3('下部（占4）— 设备状态饼图')
bullet('使用饼图统计各使用状态的设备数量及占比。')
bullet('右侧显示四种状态的颜色圆点标识及对应数量（台）。')
make_table(
    ['状态', '说明', '示例数量'],
    [
        ['使用', '当前正在使用中的设备', '486 台'],
        ['空闲', '空置未使用的设备', '1,840 台'],
        ['正常', '状态正常待命的设备', '98 台'],
        ['维修', '正在维修或故障设备', '34 台'],
    ],
    col_widths=[3, 8, 4.5]
)

divider()

# ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
# 8. 3区
# ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
heading1('8. 3区 — 实时监控')
body('3区是大屏中央最大区域（比例占4.5），固定显示实时监控视图，分为左侧面板和右侧画面区两部分，无需切换。')

heading2('8.1 左侧面板（功能操作区）')
make_table(
    ['功能组件', '说明'],
    [
        ['搜索框', '可输入关键词搜索楼栋、楼层或房间名称'],
        ['二级单位下拉筛选', '下拉选择框，可按化学研究所/物理研究所等单位进行筛选过滤'],
        ['建筑结构树状图', '可展开/折叠的树形结构，层级为：院区 > 楼栋 > 楼层 > 房间\n点击节点可选中并高亮对应监控画面'],
    ],
    col_widths=[4, 11.5]
)

heading2('8.2 右侧画面区（9宫格监控）')
bullet('标题行：左侧显示当前位置面包屑导航（院区 › 楼栋名称 › 楼层），右侧显示翻页按钮（‹ / ›）及页码信息。')
bullet('9宫格画面：以3×3网格排列9路摄像头画面（16:9比例），每路画面使用Canvas模拟实时视频。')
bullet('第一路摄像头（AI摄像头）特殊标注：')
bullet('顶部左角：绿色"🤖 AI检测"徽标', 1)
bullet('顶部右角：红色 REC 录制指示灯（闪烁）', 1)
bullet('画面中叠加红色危险行为检测框及标签（如"危险行为: 未佩戴防护"）', 1)
bullet('其余8路摄像头显示常规画面（带室内场景模拟）。')

note('9宫格摄像头画面为Canvas模拟渲染，不接入真实摄像头信号，如需接入真实视频流请联系IT运维人员进行配置。')
divider()

# ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
# 9. 4区
# ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
heading1('9. 4区 — 实验环境感知与风险预警')
body('4区位于大屏最右侧，由上至下按7:3比例划分为两个功能模块。')

heading2('9.1 实验环境安全智能感知（占7）')
body('本模块展示每间实验室硬件传感器的实时监测数据，区域内由下向上自动滚动展示所有实验室条目（30秒一循环，鼠标悬停自动暂停）。')
doc.add_paragraph()
heading3('每条实验室条目包含以下信息：')
make_table(
    ['信息字段', '说明', '示例'],
    [
        ['实验室名称', '实验室中文名称（房号）', '化学分析实验室（A301）'],
        ['所属单位', '二级单位名称', '化学研究所'],
        ['告警状态', '正常/告警指示图标及文字', '● 正常 / 🚨 告警（闪烁）'],
        ['温度 T', '当前室内温度（°C）', '22.5°C'],
        ['湿度 H', '当前室内相对湿度（%RH）', '58%'],
        ['TVOC', '总挥发性有机化合物浓度（mg/m³）\n超标（>0.6）时标红闪烁', '0.82 mg/m³（红色告警）'],
        ['CO₂', '二氧化碳浓度（ppm）\n超标（>700）时标红闪烁', '650 ppm'],
        ['O₂', '氧气浓度（%）', '20.9%'],
    ],
    col_widths=[3, 7, 5.5]
)

body('当某传感器数值超出阈值时，该实验室条目整体高亮为红色渐变效果，并触发全屏预警弹窗（详见第10章）。')

divider()
heading2('9.2 实验室实时风险预警（占3）')
body('本模块展示本月预警响应总数及实时预警通知轮播（22秒一循环，最新通知始终显示在最上方）。')
doc.add_paragraph()
heading3('顶部统计')
bullet('本月预警响应总数：以琥珀色/橙黄色高亮数字显示（示例：42次）。')

heading3('预警通知列表（每条包含）')
make_table(
    ['字段', '说明'],
    [
        ['实验室信息', '实验室名称（房号）- 所属二级单位'],
        ['异常指标', '异常传感器类型及当前超标值'],
        ['预警时间', '精确到秒的时间戳（日期-时-分-秒）'],
    ],
    col_widths=[4, 11.5]
)

note('预警区域字体采用琥珀/橙色调（#fcd34d / #fb923c），不使用红色字体，区别于传感器告警区域。')
divider()

# ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
# 10. 全屏预警弹窗
# ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
heading1('10. 全屏预警弹窗')
body('当传感器监测数据超出设定阈值，系统将自动弹出全屏预警弹窗（系统启动约5秒后自动触发演示），以科幻电影系统报警样式呈现。')
doc.add_paragraph()

heading2('弹窗布局')
body('弹窗分为左右两个区域：')
make_table(
    ['区域', '内容', '说明'],
    [
        ['左侧', '告警实验室实时监控画面\n（900×700 Canvas模拟）', '带红色扫描线、噪点效果；\n叠加AI危险行为检测框；\n顶部显示摄像头编号、REC录制状态；\n底部显示实验室名称及AI检测标识'],
        ['右侧', '告警详细信息', '告警实验室、所属单位、告警指标、当前值/安全阈值四项信息卡片；\n底部显示紧急疏散提示文字（闪烁动效）'],
    ],
    col_widths=[2, 5, 8.5]
)

heading2('弹窗操作按钮')
make_table(
    ['按钮', '功能'],
    [
        ['稍后处理', '关闭弹窗，暂时忽略告警（后续可在4区预警列表查看）'],
        ['确认处理', '关闭弹窗，标记告警已处理'],
    ],
    col_widths=[4, 11.5]
)

heading2('弹窗视觉特征')
bullet('全屏深红色半透明遮罩，背景模糊（backdrop-filter: blur）。')
bullet('弹窗主体为深红渐变边框，红色扫描线从上到下持续扫描（1.5秒一循环）。')
bullet('标题"⚡ 系统预警 · ALERT"红色字体，💥 图标快速闪烁动效。')

note('弹窗为全屏展示，关闭弹窗后系统恢复正常监控状态，预警信息仍保留在4区实时风险预警列表中。')
divider()

# ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
# 11. 通用交互说明
# ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
heading1('11. 通用交互说明')
make_table(
    ['交互行为', '触发方式', '效果说明'],
    [
        ['全屏显示', '页面加载时自动触发', '请求浏览器进入全屏模式（F11手动切换亦可）'],
        ['图表悬停提示', '鼠标悬停在图表区域', '显示详细数据悬浮提示框（Tooltip）'],
        ['安全分级柱状图滚动', '自动（每5秒）', '自动向左滚动1个X轴单位，循环展示'],
        ['传感器列表滚动', '自动（30秒一循环）', '由下向上平滑滚动，鼠标悬停可暂停'],
        ['预警通知滚动', '自动（22秒一循环）', '由下向上平滑滚动，鼠标悬停可暂停'],
        ['树状图展开/折叠', '点击监控视图左侧树状图节点', '展开或折叠对应子节点'],
        ['关闭预警弹窗', '点击"确认处理"或"稍后处理"按钮', '关闭全屏预警弹窗，恢复正常显示'],
    ],
    col_widths=[4, 5, 6.5]
)
divider()

# ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
# 12. 数据说明
# ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
heading1('12. 数据说明')
body('当前版本大屏展示的数据均为内置模拟数据（Demo数据），用于系统功能演示。如需接入真实数据，请由IT运维人员按照以下说明进行后端数据对接。')
doc.add_paragraph()

heading2('传感器告警阈值（默认配置）')
make_table(
    ['传感器指标', '正常范围', '告警阈值', '单位'],
    [
        ['TVOC', '< 0.6', '≥ 0.6', 'mg/m³'],
        ['CO₂', '< 700', '≥ 700', 'ppm'],
        ['O₂', '19.5 ~ 23.5', '< 19.5 或 > 23.5', '%'],
        ['温度', '16 ~ 28', '< 16 或 > 28', '°C'],
        ['湿度', '30 ~ 80', '< 30 或 > 80', '% RH'],
    ],
    col_widths=[4, 4, 5, 2.5]
)

heading2('数据刷新说明')
make_table(
    ['数据模块', '刷新方式'],
    [
        ['实时时钟', '每秒刷新'],
        ['数字翻牌器（进入人数）', '页面加载时动画计数（2秒内完成）'],
        ['传感器数据列表', '页面加载时渲染（静态模拟数据）'],
        ['风险预警列表', '页面加载时渲染（静态模拟数据）'],
        ['ECharts图表', '页面加载时初始化渲染（静态模拟数据）'],
        ['柱状图自动滚动', '每5秒自动切换1个X轴步长'],
    ],
    col_widths=[6, 9.5]
)

divider()

# ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
# 13. FAQ
# ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
heading1('13. 常见问题（FAQ）')

faqs = [
    ('Q1：打开页面后图表区域空白，没有显示图表？',
     '请检查网络连接是否正常，确保可以访问 cdn.jsdelivr.net（ECharts CDN地址）。如无外网访问条件，请将 ECharts JS 文件下载至本地，并将 HTML 文件头部的 CDN 引用路径改为本地相对路径。'),
    ('Q2：页面一打开就弹出了预警弹窗，如何关闭？',
     '这是系统的演示效果，页面加载约5秒后会自动弹出全屏预警弹窗。点击弹窗右下角的"确认处理"或"稍后处理"按钮即可关闭。'),
    ('Q3：3区实时监控画面不显示或摄像头为黑屏？',
     '9宫格摄像头画面为Canvas模拟渲染，页面加载完成后自动初始化。若画面为黑屏，请刷新页面后等待3~5秒。如需接入真实摄像头，请联系IT运维人员进行后端视频流配置。'),
    ('Q4：页面内容显示不完整或过小，无法正常查看？',
     '本大屏设计分辨率为 9600×2800px，需配合超大屏/拼接屏使用。普通1080p显示器需通过浏览器滚动查看完整内容，或将浏览器缩放比例调整为25%~33%以适配屏幕。'),
    ('Q5：9宫格监控画面是否为真实摄像头接入？',
     '当前版本为Canvas模拟画面，不接入真实摄像头信号。如需接入真实CCTV/RTSP视频流，需进行后端视频推流对接开发，请联系IT运维人员处理。'),
    ('Q6：传感器数据是否实时更新？',
     '当前版本数据为静态模拟数据，页面加载后不会自动刷新传感器数值。如需接入真实IoT传感器数据，需配置WebSocket或轮询接口，由IT运维人员进行后端集成开发。'),
    ('Q7：如何修改告警阈值？',
     '当前阈值配置在 index-v2.html 的 JavaScript 代码中（LABS数组及判断逻辑），修改对应字段的判断条件即可。生产环境建议将阈值配置抽取为后端可配置参数。'),
]

for q, a in faqs:
    p = doc.add_paragraph()
    p.paragraph_format.space_before = Pt(8)
    p.paragraph_format.space_after  = Pt(2)
    add_run(p, q, bold=True, size=11, color=C_H2)
    p2 = doc.add_paragraph()
    p2.paragraph_format.space_before = Pt(2)
    p2.paragraph_format.space_after  = Pt(6)
    p2.paragraph_format.left_indent  = Cm(0.5)
    add_run(p2, 'A：' + a, size=10.5, color=C_BODY)

divider()

# ── 页脚说明 ─────────────────────────────────────────────────────
doc.add_paragraph()
p = doc.add_paragraph()
p.alignment = WD_ALIGN_PARAGRAPH.CENTER
add_run(p, '中国安全生产科学研究院  ·  实验室安全智能监测与管控中心  ·  使用说明文档 V1.0', size=9, color=C_LABEL)
p = doc.add_paragraph()
p.alignment = WD_ALIGN_PARAGRAPH.CENTER
add_run(p, '本文档仅供内部使用，未经许可不得对外发布。', size=9, italic=True, color=C_LABEL)

# ── 保存 ────────────────────────────────────────────────────────
out = r'C:\Users\Administrator\lab-safety-monitor\实验室安全大屏使用说明.docx'
doc.save(out)
print(f'已保存：{out}')
