|
|
@@ -2,6 +2,7 @@ import { useState, useRef } from 'react';
|
|
|
import * as XLSX from 'xlsx';
|
|
|
import PizZip from 'pizzip';
|
|
|
import Docxtemplater from 'docxtemplater';
|
|
|
+import ImageModule from 'docxtemplater-image-module-free';
|
|
|
import JSZip from 'jszip';
|
|
|
import { Button } from '@/client/components/ui/button';
|
|
|
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/client/components/ui/card';
|
|
|
@@ -228,9 +229,29 @@ export default function WordPreview() {
|
|
|
const arrayBuffer = await wordFile.arrayBuffer();
|
|
|
const zip = new PizZip(arrayBuffer);
|
|
|
|
|
|
+ // 配置图片模块
|
|
|
+ const imageOpts = {
|
|
|
+ centered: false,
|
|
|
+ getImage: (tagValue: string) => {
|
|
|
+ if (tagValue && typeof tagValue === 'string') {
|
|
|
+ // 从imageMappings中获取对应的图片
|
|
|
+ const folderIndex = (rowIndex + 1).toString();
|
|
|
+ const imageFile = imageMappings[folderIndex]?.[tagValue];
|
|
|
+ if (imageFile) {
|
|
|
+ return imageFile.arrayBuffer();
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return null;
|
|
|
+ },
|
|
|
+ getSize: () => [200, 150] // 固定尺寸
|
|
|
+ };
|
|
|
+
|
|
|
+ const imageModule = new ImageModule(imageOpts);
|
|
|
+
|
|
|
const doc = new Docxtemplater(zip, {
|
|
|
paragraphLoop: true,
|
|
|
linebreaks: true,
|
|
|
+ modules: [imageModule]
|
|
|
});
|
|
|
|
|
|
// 处理嵌套数据结构
|
|
|
@@ -253,24 +274,15 @@ export default function WordPreview() {
|
|
|
}
|
|
|
});
|
|
|
|
|
|
- // 处理图片字段
|
|
|
+ // 处理图片字段 - 直接传递图片名称
|
|
|
const folderIndex = (rowIndex + 1).toString();
|
|
|
if (imageMappings[folderIndex]) {
|
|
|
- for (const [imageName, imageFile] of Object.entries(imageMappings[folderIndex])) {
|
|
|
- try {
|
|
|
- processedData[`image:${imageName}`] = {
|
|
|
- data: imageFile,
|
|
|
- width: 200,
|
|
|
- height: 150
|
|
|
- };
|
|
|
- } catch (error) {
|
|
|
- console.error(`处理图片失败: ${imageName}`, error);
|
|
|
- }
|
|
|
+ for (const [imageName] of Object.entries(imageMappings[folderIndex])) {
|
|
|
+ processedData[imageName] = imageName;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- doc.setData(processedData);
|
|
|
- doc.render();
|
|
|
+ doc.render(processedData);
|
|
|
|
|
|
const generatedDoc = doc.getZip().generate({
|
|
|
type: 'blob',
|
|
|
@@ -754,7 +766,7 @@ export default function WordPreview() {
|
|
|
<div>
|
|
|
<h4 className="font-medium mb-1">1. 准备Word模板</h4>
|
|
|
<p className="text-muted-foreground">
|
|
|
- 使用 {'{{'}字段名{'}'} 格式作为文本占位符,使用 {'{{'}image:图片名{'}'} 格式作为图片占位符
|
|
|
+ 使用 {'{字段名}'} 格式作为文本占位符,使用 {'{%image:图片名%}'} 格式作为图片占位符
|
|
|
</p>
|
|
|
</div>
|
|
|
|
|
|
@@ -775,7 +787,7 @@ export default function WordPreview() {
|
|
|
<div>
|
|
|
<h4 className="font-medium mb-1">4. 图片命名规则</h4>
|
|
|
<p className="text-muted-foreground">
|
|
|
- 例如:模板中使用 {'{{'}image:logo{'}'},则图片文件应命名为 logo.jpg/png等
|
|
|
+ 例如:模板中使用 {'{%logo%}'},则图片文件应命名为 logo.jpg/png等
|
|
|
</p>
|
|
|
</div>
|
|
|
</div>
|
|
|
@@ -797,6 +809,7 @@ export default function WordPreview() {
|
|
|
<p>• 文件夹序号必须与Excel行号对应(第1行对应文件夹"1")</p>
|
|
|
<p>• 如果图片不存在,对应位置将留空</p>
|
|
|
<p>• 支持jpg、jpeg、png、gif、bmp、webp格式图片</p>
|
|
|
+ <p>• 图片占位符使用 {'{%图片名%}'} 格式,如 {'{%logo%}'}</p>
|
|
|
</div>
|
|
|
</CardContent>
|
|
|
</Card>
|