操作OOXML - docx有序列表重新编号

背景

最近要做markdown文档转word文档的功能,
由于自定义样式的要求比较高,所以没用pandas,,
emmmm,也有可能是我不熟,,,不要在意

我用的是markdown-it-py解析结构,python-docx写入内容,,
具体代码怎么实现的,这不重要,
重要的是,我遇到了一个问题,最后转出来的docx文档,有序列表是连贯的,,
这就很糟心,,然后我就开始想办法处理了...

数据

## 异常行为侦测

1. 用户点击“异常行为侦测”菜单,系统自动加载实时事件流,校验用户权限并记录访问日志,输出最新异常事件列表。  
    1. aabb
    2. ccdd
2. 用户筛选事件等级与网关,系统同步过滤数据,状态栏显示当前筛选条件,输出命中事件统计与风险指数。  
    1. aabb
    2. ccdd
3. 用户点击单条事件,系统自动调取详细信息,更新右侧风险画像,输出行为分布、建议处置与历史黑名单。  
    1. aabb
    2. ccdd
4. 用户执行“分派值守”或“一键联动”,系统校验操作权限,生成处置任务,记录操作日志并通知相关人员,输出任务状态与处置结果。  
    - aabb
    - ccdd
5. 用户导出事件数据,系统自动生成CSV文件,校验导出权限,记录导出日志,输出文件下载链接。
    - aabb
    - ccdd


## 一键封控

1. 用户点击“一键封控”,系统校验高危号段及权限,生成封控任务,记录操作日志,输出影响预估与生效时间。  
    1. aabb
    2. ccdd
2. 用户选择“双签并执行封控”,系统自动同步审批流程,校验L3双签,更新任务状态,通知相关业务方,输出封控执行结果。  
    1. aabb
    2. ccdd
3. 用户可缩短封控时长,系统校验操作合法性,调整封控策略,记录变更日志,输出新生效时间。  
    - aabb
    - ccdd
4. 操作完成后,系统自动生成封控事件报告,支持导出PDF,记录全流程日志,输出报告下载链接。
    - aabb
    - ccdd


## 事件详情

1. 用户点击异常事件,系统自动调取事件详情,校验权限并记录访问日志,输出事件关联链路与建议处置。  
2. 用户选择“加入观察名单”,系统校验操作合法性,更新名单状态,记录操作日志,通知相关监控人员,输出名单变更结果。  
3. 用户点击“导出取证”,系统生成事件证据JSON文件,校验导出权限,记录导出日志,输出下载链接。  
4. 用户应用推荐处置,系统自动同步策略,更新事件状态,记录处置日志,通知业务方,输出处置结果。

过程

当然是直接上chatgpt了,,hhh,中途换了codex,给的代码都没用,,
也查了python-docx文档,说是目前不支持有序列表重新编号,,

最后我用了万能的控制变量法,,
将导出的docx复制了一份,手动重新编号,然后解压,
挨个查看xml的区别,再一点点修改,看有没有效果,,

这里推荐vscode控件OOXML Viewer
可以直接修改,然后双击打开docx查看效果,不需要重命名,解压,打包这一串复杂操作

最后找到了重新编号的办法,,

结构

这就是docx解压后的文件夹结构,我们需要关注的有两个document.xmlnumbering.xml
image
正常的有序列表结构如下,在document.xml

    <w:p w14:paraId="02CF8387">
      <w:pPr>
        <w:pStyle w:val="14"/>
        <w:kinsoku/>
        <w:overflowPunct/>
        <w:adjustRightInd/>
        <w:snapToGrid/>
        <w:spacing w:before="0" w:after="0" w:line="240" w:lineRule="auto"/>
        <w:jc w:val="left"/>
        <w:textAlignment w:val="auto"/>
      </w:pPr>
      <w:r>
        <w:rPr>
          <w:rFonts w:ascii="微软雅黑" w:hAnsi="微软雅黑" w:eastAsia="微软雅黑" w:cs="微软雅黑"/>
          <w:b w:val="0"/>
          <w:i w:val="0"/>
          <w:color w:val="000000"/>
          <w:sz w:val="22"/>
          <w:u w:val="none"/>
          <w:lang w:val="en-US" w:eastAsia="zh-CN"/>
        </w:rPr>
        <w:t>用户点击“一键封控”,系统校验高危号段及权限,生成封控任务,记录操作日志,输出影响预估与生效时间。</w:t>
      </w:r>
    </w:p>

如果需要重新编号的话,需要改为以下结构,需注意的标签为w:numPr

    <w:p w14:paraId="02CF8387">
      <w:pPr>
        <w:pStyle w:val="14"/>
        <w:numPr>
          <w:numId w:val="56258"/>
        </w:numPr>
        <w:kinsoku/>
        <w:overflowPunct/>
        <w:adjustRightInd/>
        <w:snapToGrid/>
        <w:spacing w:before="0" w:after="0" w:line="240" w:lineRule="auto"/>
        <w:jc w:val="left"/>
        <w:textAlignment w:val="auto"/>
      </w:pPr>
      <w:r>
        <w:rPr>
          <w:rFonts w:ascii="微软雅黑" w:hAnsi="微软雅黑" w:eastAsia="微软雅黑" w:cs="微软雅黑"/>
          <w:b w:val="0"/>
          <w:i w:val="0"/>
          <w:color w:val="000000"/>
          <w:sz w:val="22"/>
          <w:u w:val="none"/>
          <w:lang w:val="en-US" w:eastAsia="zh-CN"/>
        </w:rPr>
        <w:t>用户点击“一键封控”,系统校验高危号段及权限,生成封控任务,记录操作日志,输出影响预估与生效时间。</w:t>
      </w:r>
    </w:p>

经过我的测试,这个numId的val,放一个随机正整数都可以生效,

相对的,需要在numbering.xml中添加内容,只要numId能对上即可

  <w:num w:numId="56258">
    <w:abstractNumId w:val="7"/>
    <w:lvlOverride w:ilvl="0">
      <w:startOverride w:val="1"/>
    </w:lvlOverride>
  </w:num>

w:lvlOverride w:ilvl="0"这是编号层级,0代表第一层

w:startOverride w:val="1"决定的是重新编号后的起始值

w:abstractNumId w:val="7"决定的是编号模板,格式如下,各字段释义询问ai

  <w:abstractNum w:abstractNumId="7">
    <w:nsid w:val="FFFFFF88"/>
    <w:multiLevelType w:val="singleLevel"/>
    <w:tmpl w:val="D0A62B40"/>
    <w:lvl w:ilvl="0">
      <w:start w:val="1"/>
      <w:numFmt w:val="decimal"/>
      <w:pStyle w:val="ListNumber"/>
      <w:lvlText w:val="%1."/>
      <w:lvlJc w:val="left"/>
      <w:pPr>
        <w:tabs>
          <w:tab w:val="num" w:pos="360"/>
        </w:tabs>
        <w:ind w:left="360" w:hanging="360"/>
      </w:pPr>
    </w:lvl>
  </w:abstractNum>

以上是结构说明

代码操作

我先在numbering.xml中写入了一个自定义的abstractNum,
这个结构支持三层有序列表,不够可以自己加,样式可以让ai改,

  <w:abstractNum w:abstractNumId="999">
    <w:multiLevelType w:val="multilevel"/>
    <!-- 一级 -->
    <w:lvl w:ilvl="0">
      <w:start w:val="1"/>
      <w:numFmt w:val="decimal"/>
      <w:lvlText w:val="%1."/>
      <w:lvlJc w:val="left"/>
      <w:pPr>
        <w:ind w:left="360" w:hanging="360"/>
      </w:pPr>
    </w:lvl>
    <!-- 二级 -->
    <w:lvl w:ilvl="1">
      <w:start w:val="1"/>
      <w:numFmt w:val="decimal"/>
      <w:lvlText w:val="%1.%2"/>
      <w:lvlJc w:val="left"/>
      <w:pPr>
        <w:ind w:left="720" w:hanging="360"/>
      </w:pPr>
    </w:lvl>
    <!-- 三级 -->
    <w:lvl w:ilvl="2">
      <w:start w:val="1"/>
      <w:numFmt w:val="decimal"/>
      <w:lvlText w:val="%1.%2.%3"/>
      <w:lvlJc w:val="left"/>
      <w:pPr>
        <w:ind w:left="1080" w:hanging="360"/>
      </w:pPr>
    </w:lvl>
  </w:abstractNum>

然后在document.xml中,为每项有序列表绑定numId(随机数)

<!-- 第一层,第一项 -->
<w:numPr>
    <w:ilvl w:val="0"/>
    <w:numId w:val="97281"/>
</w:numPr>
<!-- 第一层,第N项 -->
<w:numPr>
    <w:ilvl w:val="0"/>
    <w:numId w:val="52486"/>
</w:numPr>
<!-- 第二层,第一项 -->
<w:numPr>
    <w:ilvl w:val="1"/>
    <w:numId w:val="98496"/>
</w:numPr>
<!-- 第二层,第N项 -->
<w:numPr>
    <w:ilvl w:val="1"/>
    <w:numId w:val="25463"/>
</w:numPr>

<!-- 以此类推 -->

接着继续往numbering.xml写入内容,将numId绑定到自定义的abstractNum上,
变动的内容就两个,一个是numId,一个是w:ilvl

<!-- 第一层,第一项 -->
<w:num w:numId="97281">
    <w:abstractNumId w:val="999"/>
    <w:lvlOverride w:ilvl="0">
        <w:startOverride w:val="1"/>
    </w:lvlOverride>
</w:num>
<!-- 第一层,第N项 -->
<w:num w:numId="52486">
    <w:abstractNumId w:val="999"/>
</w:num>
<!-- 第二层,第一项 -->
<w:num w:numId="98496">
    <w:abstractNumId w:val="999"/>
    <w:lvlOverride w:ilvl="1">
        <w:startOverride w:val="1"/>
    </w:lvlOverride>
</w:num>
<!-- 第二层,第N项 -->
<w:num w:numId="25463">
    <w:abstractNumId w:val="999"/>
</w:num>

<!-- 以此类推 -->

最后打包成zip、再重命名为docx就行了,,,


好了,我知道自己菜,大家凑合看看就行了,,
这里仅作记录,

posted @ 2026-07-07 14:12  步尘  阅读(13)  评论(0)    收藏  举报