|
@@ -1,148 +1,197 @@
|
|
|
-import _ from 'lodash'
|
|
|
-import { saveAs } from 'file-saver'
|
|
|
-import { Document, Packer, Paragraph, TextRun, Table, TableCell, TableRow, WidthType } from "docx";
|
|
|
-
|
|
|
-
|
|
|
+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" })],
|
|
|
+ [_.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)
|
|
|
-}
|
|
|
-
|
|
|
-
|
|
|
+ ])(node);
|
|
|
+};
|
|
|
|
|
|
-const buildDocxParagraph = (node, context = {}) => new Paragraph({ ...buildParagraphOptions(node, context), children: _.flatMap(node.childNodes, c => buildDocxTextRun(c)) })
|
|
|
+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 {}
|
|
|
-}
|
|
|
+ return {};
|
|
|
+};
|
|
|
|
|
|
// eslint-disable-next-line
|
|
|
-const buildDocxTextRun = (node, context = {}) => new TextRun({ text: node.textContent.trim(), ...buildTextRunOptions(node) })
|
|
|
-
|
|
|
-
|
|
|
+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(_.get(node, ['properties', 'form_card_data'], []), 3)
|
|
|
- return chunkedFields.map((fields) =>
|
|
|
- new TableRow({
|
|
|
- children: _.flatten(
|
|
|
- _.assign(
|
|
|
- _.fill(new Array(3), [new TableCell({ children: [], columnSpan: 2 })]),
|
|
|
- fields?.map(field => {
|
|
|
+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 TableCell({
|
|
|
- children: [new Paragraph(field.value)],
|
|
|
- width: { size: 10, type: WidthType.PERCENTAGE },
|
|
|
+ new Paragraph({
|
|
|
+ bullet: { level: context.level ?? 0 },
|
|
|
+ children: _.map(preNodes, (c) => buildDocxTextRun(c)),
|
|
|
}),
|
|
|
- new TableCell({
|
|
|
- children: [new Paragraph(field.input)],
|
|
|
- width: { size: 20, type: WidthType.PERCENTAGE },
|
|
|
- })
|
|
|
- ]
|
|
|
- })
|
|
|
- )
|
|
|
- )
|
|
|
+ ..._.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 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.name)],
|
|
|
+ children: [new Paragraph(node.assistantName)],
|
|
|
width: { size: 10, type: WidthType.PERCENTAGE },
|
|
|
}),
|
|
|
new TableCell({
|
|
|
- children: convertAgentContent(node.properties?.content),
|
|
|
+ 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],
|
|
|
+ [_.matches({ type: "form_card" }), buildFormCardRows],
|
|
|
// Agent Node
|
|
|
- [_.matches({ type: 'UserTask' }), buildAgentRows],
|
|
|
- [_.stubTrue, _.constant([])]
|
|
|
- ])
|
|
|
- return _.flatten(nodes.map(sectionBuilder))
|
|
|
-}
|
|
|
+ [_.matches({ type: "UserTask" }), buildAgentRows],
|
|
|
+ [_.stubTrue, _.constant([])],
|
|
|
+ ]);
|
|
|
+ return _.flatten(nodes.map(sectionBuilder));
|
|
|
+};
|
|
|
|
|
|
export const exportFlowToDocx = (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: [
|
|
|
- new Paragraph({
|
|
|
- text: "课程设计",
|
|
|
- heading: "Heading1",
|
|
|
- alignment: 'center'
|
|
|
- }),
|
|
|
- new Table({
|
|
|
- rows: buildRows(nodes)
|
|
|
- }),
|
|
|
- ],
|
|
|
- }],
|
|
|
+ sections: [
|
|
|
+ {
|
|
|
+ children,
|
|
|
+ },
|
|
|
+ ],
|
|
|
});
|
|
|
|
|
|
// 将文档打包成Buffer并保存为文件
|
|
|
Packer.toBlob(doc).then((blob) => {
|
|
|
- const mimeType = 'application/vnd.openxmlformats-officedocument.wordprocessingml.document'
|
|
|
- saveAs(blob, 'Generated.docx', { type: mimeType })
|
|
|
+ const mimeType =
|
|
|
+ "application/vnd.openxmlformats-officedocument.wordprocessingml.document";
|
|
|
+ saveAs(blob, "课程设计.docx", { type: mimeType });
|
|
|
});
|
|
|
- return
|
|
|
-}
|
|
|
+ return;
|
|
|
+};
|