feat/printTemplate (#1345)
Co-authored-by: root <root@huan.daoyoucloud.com> Reviewed-on: daoyoucloud/tachybase#1345 Reviewed-by: sealday <zhanglin@daoyoucloud.com> Co-authored-by: yoona <1486343814@qq.com> Co-committed-by: yoona <1486343814@qq.com>
This commit is contained in:
parent
eaa9bb0bd2
commit
73fdc790f4
@ -1,14 +1,14 @@
|
|||||||
import fs from 'fs';
|
import fs from 'fs';
|
||||||
import path from 'path';
|
import path from 'path';
|
||||||
import { PassThrough, Readable } from 'stream';
|
import { Readable } from 'stream';
|
||||||
import { Context } from '@tachybase/actions';
|
import { Context } from '@tachybase/actions';
|
||||||
import { Model } from '@tachybase/database';
|
import { Model } from '@tachybase/database';
|
||||||
|
|
||||||
import Docxtemplater from 'docxtemplater';
|
import Docxtemplater from 'docxtemplater';
|
||||||
|
// 导入 Docxtemplater 的 InspectModule 类
|
||||||
|
import InspectModule from 'docxtemplater/js/inspect-module';
|
||||||
import PizZip from 'pizzip';
|
import PizZip from 'pizzip';
|
||||||
|
|
||||||
import { getTemplateParams } from './template-params'; // 导入 getTemplateParams
|
|
||||||
|
|
||||||
export const generate = async (ctx: Context) => {
|
export const generate = async (ctx: Context) => {
|
||||||
const id = ctx.action.params.id;
|
const id = ctx.action.params.id;
|
||||||
const repo = ctx.db.getRepository('templateManage');
|
const repo = ctx.db.getRepository('templateManage');
|
||||||
@ -22,47 +22,23 @@ export const generate = async (ctx: Context) => {
|
|||||||
|
|
||||||
const rawTemplate = template.get();
|
const rawTemplate = template.get();
|
||||||
const templatePath = path.join(process.env.PWD, rawTemplate.template[0].get().url);
|
const templatePath = path.join(process.env.PWD, rawTemplate.template[0].get().url);
|
||||||
|
|
||||||
const data = rawTemplate.testData;
|
const data = rawTemplate.testData;
|
||||||
|
|
||||||
const outputPath = path.join(__dirname, './output.docx');
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// 获取模板中的占位符
|
|
||||||
const tags = (await getTemplateParams(templatePath)) as any;
|
|
||||||
console.log('Template tags:', tags);
|
|
||||||
|
|
||||||
// 验证数据是否包含所有占位符
|
|
||||||
const missingTags = tags.filter((tag) => !data.prototype.hasOwnProperty.call(tag.replace(/[{}]/g, '')));
|
|
||||||
if (missingTags.length > 0) {
|
|
||||||
// throw new Error(`Missing data for tags: ${missingTags.join(',')}`);
|
|
||||||
console.log('Missing data for tags');
|
|
||||||
}
|
|
||||||
|
|
||||||
// 生成定制的 docx 文件
|
// 生成定制的 docx 文件
|
||||||
const buffer = generateDocxFromTemplate(templatePath, data);
|
const buffer = generateDocxFromTemplate(templatePath, data);
|
||||||
|
|
||||||
// 创建一个可读流
|
// 创建一个可读流
|
||||||
const readableStream = new Readable({
|
const readableStream = new Readable({
|
||||||
read(size) {
|
read(size) {
|
||||||
// 将Buffer数据push到可读流中
|
|
||||||
this.push(buffer);
|
this.push(buffer);
|
||||||
// 标记流的末尾
|
|
||||||
this.push(null);
|
this.push(null);
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
ctx.withoutDataWrapping = true;
|
ctx.withoutDataWrapping = true;
|
||||||
// 设置响应类型为二进制流
|
|
||||||
ctx.set('Content-Type', 'application/octet-stream');
|
ctx.set('Content-Type', 'application/octet-stream');
|
||||||
// 将可读流设置为响应体
|
|
||||||
ctx.body = readableStream;
|
ctx.body = readableStream;
|
||||||
|
|
||||||
// // 确保生成的 docx 文件存在
|
|
||||||
// if (!fs.existsSync(outputPath)) {
|
|
||||||
// throw new Error('Generated DOCX file not found');
|
|
||||||
// }
|
|
||||||
// ctx.body = 'Word and PDF files generated';
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Error:', error);
|
console.error('Error:', error);
|
||||||
ctx.status = 500;
|
ctx.status = 500;
|
||||||
@ -70,6 +46,50 @@ export const generate = async (ctx: Context) => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const getTags = async (ctx: Context) => {
|
||||||
|
const id = ctx.action.params.id;
|
||||||
|
const repo = ctx.db.getRepository('templateManage');
|
||||||
|
const template: Model = await repo.findOne({
|
||||||
|
filter: {
|
||||||
|
id,
|
||||||
|
},
|
||||||
|
appends: ['template'],
|
||||||
|
});
|
||||||
|
const rawTemplate = template.get();
|
||||||
|
const templatePath = path.join(process.env.PWD, rawTemplate.template[0].get().url);
|
||||||
|
|
||||||
|
try {
|
||||||
|
const content = fs.readFileSync(templatePath, 'binary');
|
||||||
|
const zip = new PizZip(content);
|
||||||
|
const iModule = new InspectModule();
|
||||||
|
const doc = new Docxtemplater(zip, { modules: [iModule] });
|
||||||
|
|
||||||
|
// 获取所有占位符
|
||||||
|
const allTags = iModule.getAllTags();
|
||||||
|
console.log('All tags in the document:', allTags);
|
||||||
|
|
||||||
|
// 将 tags 转换为 JSON 格式的字符串
|
||||||
|
const tagsJson = JSON.stringify(allTags, null, 2);
|
||||||
|
|
||||||
|
// 创建一个可读流
|
||||||
|
const readableStream = new Readable({
|
||||||
|
read(size) {
|
||||||
|
this.push(tagsJson); // 将 tagsJson 写入流中
|
||||||
|
this.push(null); // 结束流
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
// 设置响应头和响应内容
|
||||||
|
ctx.withoutDataWrapping = true;
|
||||||
|
ctx.set('Content-Type', 'application/json');
|
||||||
|
ctx.body = readableStream;
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error:', error);
|
||||||
|
ctx.status = 500;
|
||||||
|
ctx.body = 'Error generating tags: ' + error.message;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
function generateDocxFromTemplate(templatePath, data): Buffer {
|
function generateDocxFromTemplate(templatePath, data): Buffer {
|
||||||
try {
|
try {
|
||||||
const content = fs.readFileSync(templatePath, 'binary');
|
const content = fs.readFileSync(templatePath, 'binary');
|
||||||
@ -77,17 +97,11 @@ function generateDocxFromTemplate(templatePath, data): Buffer {
|
|||||||
const doc = new Docxtemplater(zip);
|
const doc = new Docxtemplater(zip);
|
||||||
|
|
||||||
doc.setData(data);
|
doc.setData(data);
|
||||||
|
|
||||||
try {
|
|
||||||
doc.render();
|
doc.render();
|
||||||
} catch (error) {
|
|
||||||
console.error('Error rendering template:', error);
|
|
||||||
throw error;
|
|
||||||
}
|
|
||||||
|
|
||||||
return doc.getZip().generate({ type: 'nodebuffer' });
|
return doc.getZip().generate({ type: 'nodebuffer' });
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Error generating DOCX from template:', error);
|
console.error('Error generating DOCX from template:', error);
|
||||||
throw error; // Ensure errors are propagated correctly
|
throw error;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import { Plugin } from '@tachybase/server';
|
import { Plugin } from '@tachybase/server';
|
||||||
|
|
||||||
import { generate } from './actions/printTemplates';
|
import { generate, getTags } from './actions/printTemplates';
|
||||||
|
|
||||||
export class PluginPrintTemplateServer extends Plugin {
|
export class PluginPrintTemplateServer extends Plugin {
|
||||||
async afterAdd() {}
|
async afterAdd() {}
|
||||||
@ -13,6 +13,7 @@ export class PluginPrintTemplateServer extends Plugin {
|
|||||||
name: 'printTemplates',
|
name: 'printTemplates',
|
||||||
actions: {
|
actions: {
|
||||||
generate,
|
generate,
|
||||||
|
getTags,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
644
pnpm-lock.yaml
644
pnpm-lock.yaml
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue
Block a user