manual-generator.js 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376
  1. const fs = require('fs');
  2. const path = require('path');
  3. const { Document, Packer, Paragraph, TextRun, Table, TableRow, TableCell,
  4. Header, Footer, AlignmentType, PageOrientation, LevelFormat,
  5. HeadingLevel, BorderStyle, WidthType, ShadingType, PageNumber, PageBreak } = require('docx');
  6. const border = { style: BorderStyle.SINGLE, size: 1, color: "CCCCCC" };
  7. const borders = { top: border, bottom: border, left: border, right: border };
  8. function cell(text, width, options = {}) {
  9. return new TableCell({
  10. borders,
  11. width: { size: width, type: WidthType.DXA },
  12. shading: options.shading ? { fill: options.shading, type: ShadingType.CLEAR } : undefined,
  13. margins: { top: 80, bottom: 80, left: 120, right: 120 },
  14. verticalAlign: options.center ? "center" : undefined,
  15. children: [new Paragraph({
  16. alignment: options.center ? AlignmentType.CENTER : undefined,
  17. children: [new TextRun({ text, bold: options.bold, size: 21 })]
  18. })]
  19. });
  20. }
  21. function h1(text) {
  22. return new Paragraph({ heading: HeadingLevel.HEADING_1, children: [new TextRun(text)] });
  23. }
  24. function h2(text) {
  25. return new Paragraph({ heading: HeadingLevel.HEADING_2, children: [new TextRun(text)] });
  26. }
  27. function h3(text) {
  28. return new Paragraph({ heading: HeadingLevel.HEADING_3, children: [new TextRun(text)] });
  29. }
  30. function p(text, options = {}) {
  31. return new Paragraph({
  32. spacing: options.spacing ? { before: options.spacing[0], after: options.spacing[1] } : undefined,
  33. children: [new TextRun({ text, size: 21, bold: options.bold })]
  34. });
  35. }
  36. function bullet(text, ref) {
  37. return new Paragraph({
  38. numbering: { reference: ref, level: 0 },
  39. children: [new TextRun({ text, size: 21 })]
  40. });
  41. }
  42. const doc = new Document({
  43. styles: {
  44. default: { document: { run: { font: "Microsoft YaHei", size: 21 } } },
  45. paragraphStyles: [
  46. { id: "Heading1", name: "Heading 1", basedOn: "Normal", next: "Normal", quickFormat: true,
  47. run: { size: 36, bold: true, font: "Microsoft YaHei", color: "1F4E78" },
  48. paragraph: { spacing: { before: 400, after: 240 }, outlineLevel: 0 } },
  49. { id: "Heading2", name: "Heading 2", basedOn: "Normal", next: "Normal", quickFormat: true,
  50. run: { size: 28, bold: true, font: "Microsoft YaHei", color: "2E75B6" },
  51. paragraph: { spacing: { before: 320, after: 160 }, outlineLevel: 1 } },
  52. { id: "Heading3", name: "Heading 3", basedOn: "Normal", next: "Normal", quickFormat: true,
  53. run: { size: 24, bold: true, font: "Microsoft YaHei" },
  54. paragraph: { spacing: { before: 240, after: 120 }, outlineLevel: 2 } },
  55. ]
  56. },
  57. numbering: {
  58. config: [
  59. { reference: "bullets",
  60. levels: [{ level: 0, format: LevelFormat.BULLET, text: "•", alignment: AlignmentType.LEFT,
  61. style: { paragraph: { indent: { left: 720, hanging: 360 } } } }] },
  62. { reference: "numbers",
  63. levels: [{ level: 0, format: LevelFormat.DECIMAL, text: "%1.", alignment: AlignmentType.LEFT,
  64. style: { paragraph: { indent: { left: 720, hanging: 360 } } } }] },
  65. ]
  66. },
  67. sections: [{
  68. properties: {
  69. page: {
  70. size: { width: 12240, height: 15840 },
  71. margin: { top: 1440, right: 1440, bottom: 1440, left: 1440 }
  72. }
  73. },
  74. headers: {
  75. default: new Header({ children: [new Paragraph({
  76. alignment: AlignmentType.RIGHT,
  77. children: [new TextRun({ text: "清道夫系统操作说明书", size: 18, color: "888888" })]
  78. })] })
  79. },
  80. footers: {
  81. default: new Footer({ children: [new Paragraph({
  82. alignment: AlignmentType.CENTER,
  83. children: [
  84. new TextRun({ text: "第 ", size: 18 }),
  85. new TextRun({ children: [PageNumber.CURRENT], size: 18 }),
  86. new TextRun({ text: " 页", size: 18 })
  87. ]
  88. })] })
  89. },
  90. children: [
  91. new Paragraph({
  92. alignment: AlignmentType.CENTER,
  93. spacing: { before: 2400, after: 400 },
  94. children: [new TextRun({ text: "清道夫系统", size: 72, bold: true, color: "1F4E78", font: "Microsoft YaHei" })]
  95. }),
  96. new Paragraph({
  97. alignment: AlignmentType.CENTER,
  98. spacing: { after: 600 },
  99. children: [new TextRun({ text: "小程序端操作说明书", size: 48, color: "2E75B6", font: "Microsoft YaHei" })]
  100. }),
  101. new Paragraph({
  102. alignment: AlignmentType.CENTER,
  103. spacing: { before: 1200 },
  104. children: [new TextRun({ text: "版本号:V1.0.0", size: 24, font: "Microsoft YaHei" })]
  105. }),
  106. new Paragraph({
  107. alignment: AlignmentType.CENTER,
  108. children: [new TextRun({ text: "编制日期:2026年7月", size: 24, font: "Microsoft YaHei" })]
  109. }),
  110. new Paragraph({ children: [new PageBreak()] }),
  111. h1("目录"),
  112. new Paragraph({
  113. spacing: { before: 200, after: 100 },
  114. children: [new TextRun({ text: "1. 产品概述", size: 22 })]
  115. }),
  116. new Paragraph({
  117. spacing: { after: 100 },
  118. children: [new TextRun({ text: "2. 登录与账号", size: 22 })]
  119. }),
  120. new Paragraph({
  121. spacing: { after: 100 },
  122. children: [new TextRun({ text: "3. 通用界面说明", size: 22 })]
  123. }),
  124. new Paragraph({
  125. spacing: { after: 100 },
  126. children: [new TextRun({ text: "4. 销售端操作指南", size: 22 })]
  127. }),
  128. new Paragraph({
  129. spacing: { after: 100 },
  130. children: [new TextRun({ text: "5. 调度端操作指南", size: 22 })]
  131. }),
  132. new Paragraph({
  133. spacing: { after: 100 },
  134. children: [new TextRun({ text: "6. 施工端操作指南", size: 22 })]
  135. }),
  136. new Paragraph({
  137. spacing: { after: 100 },
  138. children: [new TextRun({ text: "7. 任务全流程说明", size: 22 })]
  139. }),
  140. new Paragraph({
  141. spacing: { after: 100 },
  142. children: [new TextRun({ text: "8. 通知公告与知识库", size: 22 })]
  143. }),
  144. new Paragraph({
  145. spacing: { after: 100 },
  146. children: [new TextRun({ text: "9. 常见问题", size: 22 })]
  147. }),
  148. new Paragraph({ children: [new PageBreak()] }),
  149. h1("1. 产品概述"),
  150. h2("1.1 应用简介"),
  151. p("清道夫系统是一款面向环境服务/市政管道疏通/化粪池清掏等业务场景的移动端作业管理平台。系统通过微信小程序为载体,实现从任务发布、审核调度、现场执行到完工确认的全流程闭环管理。"),
  152. h2("1.2 适用对象"),
  153. new Table({
  154. width: { size: 9360, type: WidthType.DXA },
  155. columnWidths: [2340, 2340, 4680],
  156. rows: [
  157. new TableRow({ children: [cell("角色", 2340, { bold: true, shading: "D5E8F0", center: true }), cell("登录端点", 2340, { bold: true, shading: "D5E8F0", center: true }), cell("核心职责", 4680, { bold: true, shading: "D5E8F0", center: true })] }),
  158. new TableRow({ children: [cell("销售/业务员", 2340), cell("mp-sales", 2340), cell("项目库管理、客户管理、合同管理、任务发布、任务跟踪、催单", 4680)] }),
  159. new TableRow({ children: [cell("调度员", 2340), cell("mp-dispatch", 2340), cell("任务审核、班组/车辆调度、排班管理、车辆管理、可视化数据、应急管理", 4680)] }),
  160. new TableRow({ children: [cell("施工人员/班组", 2340), cell("mp-construction", 2340), cell("接收任务、执行SOP标准流程、每日车辆检查、现场拍照、完工确认", 4680)] }),
  161. ]
  162. }),
  163. h2("1.3 运行环境"),
  164. bullet("支持平台:微信小程序(主要)、H5兼容", "bullets"),
  165. bullet("前端框架:uni-app + Vue3 + Pinia", "bullets"),
  166. bullet("网络要求:需要联网使用,建议使用4G/5G或Wi-Fi", "bullets"),
  167. new Paragraph({ children: [new PageBreak()] }),
  168. h1("2. 登录与账号"),
  169. h2("2.1 首次登录"),
  170. bullet("打开微信小程序,进入登录页面。", "numbers"),
  171. bullet("在账号输入框中输入管理员分配的账号。", "numbers"),
  172. bullet("在密码输入框中输入对应密码,可点击眼睛图标切换显示/隐藏。", "numbers"),
  173. bullet("如需记住密码,勾选“记住密码”选项。", "numbers"),
  174. bullet("点击“登录”按钮。", "numbers"),
  175. bullet("系统后端自动识别账号角色,跳转至对应首页。", "numbers"),
  176. h2("2.2 角色说明"),
  177. p("系统采用后端按账号识别角色的方式,用户无需手动选择角色。登录后根据账号绑定的可登录端点自动进入对应角色首页。不同角色看到的界面、功能菜单和操作权限均不同。"),
  178. h2("2.3 退出登录"),
  179. bullet("进入“我的”页面。", "bullets"),
  180. bullet("点击“退出登录”。", "bullets"),
  181. bullet("确认后返回登录页面。", "bullets"),
  182. new Paragraph({ children: [new PageBreak()] }),
  183. h1("3. 通用界面说明"),
  184. h2("3.1 底部导航栏"),
  185. p("系统采用自定义底部导航栏,根据角色显示不同内容:"),
  186. new Table({
  187. width: { size: 9360, type: WidthType.DXA },
  188. columnWidths: [1560, 2340, 2340, 3120],
  189. rows: [
  190. new TableRow({ children: [cell("索引", 1560, { bold: true, shading: "D5E8F0", center: true }), cell("页面", 2340, { bold: true, shading: "D5E8F0", center: true }), cell("文字", 2340, { bold: true, shading: "D5E8F0", center: true }), cell("说明", 3120, { bold: true, shading: "D5E8F0", center: true })] }),
  191. new TableRow({ children: [cell("0", 1560, { center: true }), cell("pages/home/home", 2340), cell("首页", 2340), cell("所有角色共用,内容按角色区分", 3120)] }),
  192. new TableRow({ children: [cell("1", 1560, { center: true }), cell("pages/task/task", 2340), cell("任务", 2340), cell("所有角色共用,列表按角色区分", 3120)] }),
  193. new TableRow({ children: [cell("2", 1560, { center: true }), cell("pages/schedule/schedule", 2340), cell("排班", 2340), cell("主要供调度端使用", 3120)] }),
  194. new TableRow({ children: [cell("3", 1560, { center: true }), cell("pages/my/my", 2340), cell("我的", 2340), cell("所有角色共用", 3120)] }),
  195. ]
  196. }),
  197. p("注:调度员角色的Tab顺序为首页、排班、任务、我的。"),
  198. h2("3.2 通用操作"),
  199. bullet("搜索框:在任务、项目、客户、合同等列表页顶部,输入关键词可快速检索。", "bullets"),
  200. bullet("筛选器:点击状态下拉框,可按状态筛选列表内容。", "bullets"),
  201. bullet("左滑操作:部分列表项支持左滑显示编辑、删除等快捷操作。", "bullets"),
  202. bullet("拨打电话:详情页点击联系电话可直接拨号。", "bullets"),
  203. bullet("地图定位:任务详情中点击地理位置可唤起导航。", "bullets"),
  204. new Paragraph({ children: [new PageBreak()] }),
  205. h1("4. 销售端操作指南"),
  206. h2("4.1 销售端首页"),
  207. p("销售登录后进入首页,可查看:"),
  208. bullet("统计卡片:本月项目数、待确认任务数、进行中任务数。", "bullets"),
  209. bullet("快捷入口:项目库、客户管理、合同管理。", "bullets"),
  210. bullet("任务提醒入口与最新任务列表。", "bullets"),
  211. bullet("通知公告列表与行业知识列表。", "bullets"),
  212. h2("4.2 发布任务"),
  213. bullet("进入“任务”页面,点击右上角“+”按钮。", "numbers"),
  214. bullet("填写项目基本信息:任务名称、任务类型、选择项目、主联系人姓名、电话。", "numbers"),
  215. bullet("填写服务信息:故障位置、地理位置、故障类型、客户原话、故障描述、首次出现时间、近期反复情况、影响范围、故障图片/视频、施工类型。", "numbers"),
  216. bullet("填写服务与结算数据:期望到达时间、最晚处理时限、验收标准、结算方式、堵点个数、清淤方量。", "numbers"),
  217. bullet("确认工单生成数据:施工指令、风险提示、派单结果判定、施工设施、服务方式、建议出车、取水距离、化粪池排口是否正常。", "numbers"),
  218. bullet("填写施工助力信息:预计作业量、预计工时、验收要求、是否有前期施工关联、验收方式、甲方当事人、备注。", "numbers"),
  219. bullet("点击提交,任务状态变为“待审核”。", "numbers"),
  220. h2("4.3 任务跟踪与催单"),
  221. bullet("在“任务”列表中查看已发布任务的状态。", "bullets"),
  222. bullet("点击任务卡片进入任务详情。", "bullets"),
  223. bullet("点击“催单”按钮,填写催单原因后提交。", "bullets"),
  224. h2("4.4 项目库、客户、合同管理"),
  225. bullet("项目库:查看、新增、编辑、删除项目信息,支持按合作类型筛选。", "bullets"),
  226. bullet("客户管理:维护客户资料,支持按状态筛选。", "bullets"),
  227. bullet("合同管理:维护合同信息,支持按状态筛选。", "bullets"),
  228. new Paragraph({ children: [new PageBreak()] }),
  229. h1("5. 调度端操作指南"),
  230. h2("5.1 调度端首页"),
  231. p("调度员登录后进入首页,可查看:"),
  232. bullet("统计卡片:今日任务总数、待排班数量、应急中任务数量。", "bullets"),
  233. bullet("快捷入口:排班管理、任务列表、车辆管理、可视化。", "bullets"),
  234. bullet("待审核任务列表与消息通知铃铛(显示未读数)。", "bullets"),
  235. h2("5.2 审核任务"),
  236. bullet("在首页或任务列表查看“待审核”任务。", "numbers"),
  237. bullet("点击任务进入详情页。", "numbers"),
  238. bullet("查看任务基本信息、项目信息、故障详情、施工信息等。", "numbers"),
  239. bullet("点击“审核通过”,任务进入待安排状态;点击“驳回”,填写驳回原因。", "numbers"),
  240. h2("5.3 安排班组与车辆"),
  241. bullet("审核通过后,在任务详情中点击“安排”。", "numbers"),
  242. bullet("选择执行班组。", "numbers"),
  243. bullet("选择调度车辆(可多选)。", "numbers"),
  244. bullet("确认后任务状态变为“已安排/待确认”。", "numbers"),
  245. h2("5.4 排班管理"),
  246. bullet("进入底部“排班”Tab。", "numbers"),
  247. bullet("使用左右箭头切换月份,点击“今天”快速回到当前日期。", "numbers"),
  248. bullet("点击“新增排班”。", "numbers"),
  249. bullet("选择班组、工作日期、排班类型(月计划/应急)、车辆,填写备注。", "numbers"),
  250. bullet("确认保存后,日历上对应日期显示排班标记。", "numbers"),
  251. h2("5.5 车辆管理"),
  252. bullet("通过首页快捷入口进入车辆列表。", "bullets"),
  253. bullet("可按状态筛选:全部、可用、使用中、维修中。", "bullets"),
  254. bullet("点击车辆卡片查看车辆详情弹窗,包含车牌号、类型、尺寸、载重、容积、驾驶员、电话、购车日期、里程、状态等。", "bullets"),
  255. new Paragraph({ children: [new PageBreak()] }),
  256. h1("6. 施工端操作指南"),
  257. h2("6.1 施工端首页"),
  258. p("施工人员登录后进入首页,可查看:"),
  259. bullet("统计卡片:已完成任务数、进行中任务数、明日任务数。", "bullets"),
  260. bullet("快捷入口:我的任务、车辆检查、帮助中心。", "bullets"),
  261. bullet("每日任务入口(带红点提醒)、当前任务列表、通知公告列表。", "bullets"),
  262. h2("6.2 接收与确认任务"),
  263. bullet("在首页或任务列表查看分配的任务。", "numbers"),
  264. bullet("点击任务进入任务详情。", "numbers"),
  265. bullet("在“确认任务”步骤选择:确认任务、反馈问题或拒绝任务。", "numbers"),
  266. h2("6.3 SOP八步执行流程"),
  267. p("施工端任务详情内置标准SOP流程,共8步:"),
  268. new Table({
  269. width: { size: 9360, type: WidthType.DXA },
  270. columnWidths: [1200, 2160, 6000],
  271. rows: [
  272. new TableRow({ children: [cell("步骤", 1200, { bold: true, shading: "D5E8F0", center: true }), cell("节点", 2160, { bold: true, shading: "D5E8F0", center: true }), cell("操作说明", 6000, { bold: true, shading: "D5E8F0", center: true })] }),
  273. new TableRow({ children: [cell("1", 1200, { center: true }), cell("确认任务", 2160), cell("确认/反馈问题/拒绝任务", 6000)] }),
  274. new TableRow({ children: [cell("2", 1200, { center: true }), cell("出车准备", 2160), cell("完成车辆点检:机油、轮胎、灯光、水管、工具", 6000)] }),
  275. new TableRow({ children: [cell("3", 1200, { center: true }), cell("到达现场", 2160), cell("现场踏勘:对接人是否到位、现场是否一致、客户是否同意施工", 6000)] }),
  276. new TableRow({ children: [cell("4", 1200, { center: true }), cell("开始施工", 2160), cell("填写堵塞物情况描述", 6000)] }),
  277. new TableRow({ children: [cell("5", 1200, { center: true }), cell("施工完成", 2160), cell("填写设施反馈描述", 6000)] }),
  278. new TableRow({ children: [cell("6", 1200, { center: true }), cell("现场清洗", 2160), cell("推进流程,可上传照片", 6000)] }),
  279. new TableRow({ children: [cell("7", 1200, { center: true }), cell("验收签字", 2160), cell("推进流程,可上传照片", 6000)] }),
  280. new TableRow({ children: [cell("8", 1200, { center: true }), cell("完工确认", 2160), cell("完成任务,状态变为已完成", 6000)] }),
  281. ]
  282. }),
  283. p("每步均可上传现场照片(最多6张)和填写备注。"),
  284. h2("6.4 每日车辆检查"),
  285. bullet("通过首页“车辆检查”或“每日任务”入口进入。", "numbers"),
  286. bullet("查看今日排班信息和分配车辆。", "numbers"),
  287. bullet("选择检查车辆。", "numbers"),
  288. bullet("逐项完成常规检查(7项)、设备检查(6项)、三大系统检查(GPS/视频监控/对讲系统)。", "numbers"),
  289. bullet("填写备注,点击提交。", "numbers"),
  290. new Paragraph({ children: [new PageBreak()] }),
  291. h1("7. 任务全流程说明"),
  292. h2("7.1 日常维保任务流程"),
  293. bullet("销售根据合同项目发起定期清掏/疏通任务。", "numbers"),
  294. bullet("任务提交后状态为“待审核”。", "numbers"),
  295. bullet("调度员审核任务,通过后安排班组和车辆。", "numbers"),
  296. bullet("施工班组接收任务并按SOP八步流程执行。", "numbers"),
  297. bullet("施工完成后进行验收签字与完工确认。", "numbers"),
  298. h2("7.2 应急抢修任务流程"),
  299. bullet("销售或调度发起应急任务。", "numbers"),
  300. bullet("调度快速安排应急班组和车辆。", "numbers"),
  301. bullet("施工人员立即出车,按简化流程或完整SOP执行。", "numbers"),
  302. bullet("现场施工完成后进行验收与完工确认。", "numbers"),
  303. h2("7.3 状态流转说明"),
  304. new Table({
  305. width: { size: 9360, type: WidthType.DXA },
  306. columnWidths: [3120, 6240],
  307. rows: [
  308. new TableRow({ children: [cell("状态", 3120, { bold: true, shading: "D5E8F0", center: true }), cell("含义", 6240, { bold: true, shading: "D5E8F0", center: true })] }),
  309. new TableRow({ children: [cell("待审核", 3120), cell("销售提交任务后等待调度审核", 6240)] }),
  310. new TableRow({ children: [cell("已驳回", 3120), cell("调度审核未通过,需销售修改后重新提交", 6240)] }),
  311. new TableRow({ children: [cell("已安排/待确认", 3120), cell("调度已安排班组车辆,等待施工确认", 6240)] }),
  312. new TableRow({ children: [cell("进行中", 3120), cell("施工班组已确认并正在执行SOP流程", 6240)] }),
  313. new TableRow({ children: [cell("已完成", 3120), cell("任务全部流程执行完毕", 6240)] }),
  314. new TableRow({ children: [cell("已取消", 3120), cell("任务被取消", 6240)] }),
  315. ]
  316. }),
  317. new Paragraph({ children: [new PageBreak()] }),
  318. h1("8. 通知公告与知识库"),
  319. h2("8.1 通知公告"),
  320. bullet("入口:首页通知列表或“我的”页面“通知公告”。", "bullets"),
  321. bullet("可按类型筛选:全部、系统、应急、任务。", "bullets"),
  322. bullet("列表显示标题、发布人、发布时间、类型标签。", "bullets"),
  323. bullet("左滑可标记已读,点击进入详情。", "bullets"),
  324. h2("8.2 行业知识库"),
  325. bullet("入口:首页行业知识列表。", "bullets"),
  326. bullet("显示知识标题、摘要、创建时间。", "bullets"),
  327. bullet("点击进入知识详情查看完整内容。", "bullets"),
  328. h2("8.3 帮助中心"),
  329. bullet("入口:首页或“我的”页面“帮助中心”。", "bullets"),
  330. bullet("按当前角色展示常见问题FAQ。", "bullets"),
  331. bullet("提供客服热线、电子邮箱、服务时间。", "bullets"),
  332. new Paragraph({ children: [new PageBreak()] }),
  333. h1("9. 常见问题"),
  334. h2("9.1 登录问题"),
  335. bullet("忘记密码:请联系管理员重置密码。", "bullets"),
  336. bullet("登录失败:请确认账号密码输入正确,区分大小写。", "bullets"),
  337. bullet("角色错误:请确认账号已绑定正确的登录端点。", "bullets"),
  338. h2("9.2 任务问题"),
  339. bullet("任务提交后无状态:请检查网络连接,或联系调度确认是否收到。", "bullets"),
  340. bullet("无法上传图片:请检查相册/相机权限是否已开启。", "bullets"),
  341. bullet("定位不准确:请检查手机定位服务是否开启。", "bullets"),
  342. h2("9.3 操作提示"),
  343. bullet("提交表单前请确认必填项已填写完整。", "bullets"),
  344. bullet("施工现场照片建议清晰、真实,便于后续验收和追溯。", "bullets"),
  345. bullet("任务执行过程中如遇异常,可通过“反馈问题”或电话联系调度。", "bullets"),
  346. h2("9.4 技术支持"),
  347. p("如遇到系统故障或其他问题,请联系技术支持团队获取帮助。"),
  348. ]
  349. }]
  350. });
  351. const outputDir = path.join(__dirname);
  352. if (!fs.existsSync(outputDir)) fs.mkdirSync(outputDir, { recursive: true });
  353. Packer.toBuffer(doc).then(buffer => {
  354. fs.writeFileSync(path.join(outputDir, "清道夫系统小程序操作说明书.docx"), buffer);
  355. console.log("文档生成成功:清道夫系统小程序操作说明书.docx");
  356. });