123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197 |
- import _ from "lodash";
- import { saveAs } from "file-saver";
- import {
- Document,
- Packer,
- Paragraph,
- TextRun,
- Table,
- TableCell,
- TableRow,
- WidthType,
- } from "docx";
- // eslint-disable-next-line
- const buildParagraphOptions = (node, context) => {
- return _.cond([
- [_.matches({ tagName: "H1" }), _.constant({ heading: "Heading1" })],
- [_.matches({ tagName: "H2" }), _.constant({ heading: "Heading2" })],
- [_.matches({ tagName: "H3" }), _.constant({ heading: "Heading3" })],
- [_.matches({ tagName: "H4" }), _.constant({ heading: "Heading4" })],
- [_.matches({ tagName: "H5" }), _.constant({ heading: "Heading5" })],
- [_.matches({ tagName: "H6" }), _.constant({ heading: "Heading6" })],
- [_.stubTrue, _.constant({})],
- ])(node);
- };
- const buildDocxParagraph = (node, context = {}) =>
- new Paragraph({
- ...buildParagraphOptions(node, context),
- children: _.flatMap(node.childNodes, (c) => buildDocxTextRun(c)),
- });
- // eslint-disable-next-line
- const buildTextRunOptions = (node) => {
- // TODO 斜体 加粗
- return {};
- };
- // eslint-disable-next-line
- const buildDocxTextRun = (node, context = {}) =>
- new TextRun({ text: node.textContent.trim(), ...buildTextRunOptions(node) });
- // NOTE this make sure root is a `Paragraph`, Paragraph can not nest Paragraph
- const buildDocxObject = (node, context = {}) =>
- _.cond([
- [
- _.matches({ nodeType: Node.ELEMENT_NODE }),
- _.cond([
- [
- _.conforms({
- tagName: (tag) =>
- ["P", "H1", "H2", "H3", "H4", "H5", "H6"].includes(tag),
- }),
- (c) => buildDocxParagraph(c),
- ],
- [
- _.matches({ tagName: "OL" }),
- (n) => _.flatMap(n.childNodes, (c) => buildDocxObject(c, context)),
- ],
- [
- _.matches({ tagName: "UL" }),
- (n) => _.flatMap(n.childNodes, (c) => buildDocxObject(c, context)),
- ],
- [
- _.matches({ tagName: "LI" }),
- (n) => {
- const childNodes = _.filter(
- n.childNodes,
- (c) => !!c.textContent.trim()
- );
- let i = _.findIndex(childNodes, (c) =>
- ["OL", "UL", "LI"].includes(c.tagName)
- );
- if (i === -1) {
- i = Infinity;
- }
- const preNodes = _.slice(childNodes, 0, i);
- const postNodes = _.slice(childNodes, i);
- return [
- new Paragraph({
- bullet: { level: context.level ?? 0 },
- children: _.map(preNodes, (c) => buildDocxTextRun(c)),
- }),
- ..._.flatMap(postNodes, (c) =>
- buildDocxObject(c, {
- level: context.level ? context.level + 1 : 1,
- })
- ),
- ];
- },
- ],
- [_.stubTrue, buildDocxParagraph],
- ]),
- ],
- [
- _.matches({ nodeType: Node.TEXT_NODE }),
- (n) => new Paragraph(n.textContent.trim()),
- ],
- [_.stubTrue, (n) => new Paragraph(n.textContent.trim())],
- ])(node);
- const buildFormCardRows = (node) => {
- const chunkedFields = _.chunk(_.values(_.get(node, ["content"], {})), 3);
- return chunkedFields.map(
- (fields) =>
- new TableRow({
- children: _.flatten(
- _.assign(
- _.fill(new Array(3), [
- new TableCell({ children: [], columnSpan: 2 }),
- ]),
- fields?.map((field) => {
- return [
- new TableCell({
- children: [new Paragraph(field.label)],
- width: { size: 10, type: WidthType.PERCENTAGE },
- }),
- new TableCell({
- children: [new Paragraph(field.value)],
- width: { size: 20, type: WidthType.PERCENTAGE },
- }),
- ];
- })
- )
- ),
- })
- );
- };
- const convertAgentContent = (content) => {
- const node = document.createElement("div");
- node.innerHTML = content;
- return _.flatMap(node.childNodes, buildDocxObject);
- };
- const buildAgentRows = (node) => {
- return [
- new TableRow({
- children: [
- new TableCell({
- children: [new Paragraph(node.assistantName)],
- width: { size: 10, type: WidthType.PERCENTAGE },
- }),
- new TableCell({
- children: convertAgentContent(node.content),
- width: { size: 90, type: WidthType.PERCENTAGE },
- columnSpan: 5,
- }),
- ],
- }),
- ];
- };
- const buildRows = (nodes) => {
- const sectionBuilder = _.cond([
- // Form Node
- [_.matches({ type: "form_card" }), buildFormCardRows],
- // Agent Node
- [_.matches({ type: "UserTask" }), buildAgentRows],
- [_.stubTrue, _.constant([])],
- ]);
- return _.flatten(nodes.map(sectionBuilder));
- };
- export const exportFlowToDocx = async (nodes) => {
- const children: unknown[] = [
- new Paragraph({
- text: "课程设计",
- heading: "Heading1",
- alignment: "center",
- }),
- ];
- const rows = buildRows(nodes);
- if (rows.length) {
- children.push(
- new Table({
- rows,
- })
- );
- }
- const doc = new Document({
- sections: [
- {
- children,
- },
- ],
- });
- // 将文档打包成Buffer并保存为文件
- return await Packer.toBlob(doc)
- // (blob) => {
- // const mimeType =
- // "application/vnd.openxmlformats-officedocument.wordprocessingml.document";
- // saveAs(blob, "课程设计.docx", { type: mimeType });
- // }
- };
|