1
1
import { useRecords , useFields , useActiveViewId , useSelection , useCloudStorage , useSettingsButton , useViewport , useField , Field , Record , IAttachmentValue , usePrimaryField , FieldType , useDatasheet } from '@vikadata/widget-sdk' ;
2
- import { Button } from '@vikadata/components' ;
2
+ import { Button , IButtonProps } from '@vikadata/components' ;
3
3
import { InformationSmallOutlined } from '@vikadata/icons' ;
4
4
import React , { useEffect , useState } from 'react' ;
5
5
import Docxtemplater from 'docxtemplater' ;
@@ -75,8 +75,12 @@ function throwError(error: any) {
75
75
/**
76
76
* 遍历已选择的多条 record ,从中获取数据并生成 word 文档
77
77
*/
78
- function generateDocuments ( selectedRecords : Record [ ] , fields : Field [ ] , selectedAttachmentField : Field , primaryField : Field ) {
78
+ async function generateDocuments ( selectedRecords : Record [ ] , fields : Field [ ] , selectedAttachmentField : Field , primaryField : Field ) {
79
+ const outputZip = new PizZip ( ) ;
79
80
81
+ // 鼠标只选择了一行
82
+ const single = selectedRecords . length > 1 ? false : true ;
83
+ var outputs = [ ]
80
84
81
85
for ( let index = 0 ; index < selectedRecords . length ; index ++ ) {
82
86
const record = selectedRecords [ index ]
@@ -92,11 +96,12 @@ function generateDocuments(selectedRecords: Record[], fields: Field[], selectedA
92
96
row [ field . name ] = row [ field . name ] . map ( item => {
93
97
return item . name
94
98
} )
99
+ } else if ( field . type == FieldType . SingleSelect ) {
100
+ const selectOption = record . getCellValue ( field . id )
101
+ row [ field . name ] = selectOption ? selectOption . name : ""
95
102
}
96
103
} )
97
104
98
-
99
-
100
105
const attachements = record . getCellValue ( selectedAttachmentField . id )
101
106
if ( ! attachements ) {
102
107
alert ( `在指定的附件字段中找不到word模板,请上传。record:[${ filename } ]` )
@@ -114,11 +119,47 @@ function generateDocuments(selectedRecords: Record[], fields: Field[], selectedA
114
119
115
120
console . log ( { row, attachements, prefix, suffix } )
116
121
117
- attachements && generateDocument ( row , attachements [ 0 ] , prefix + "-" + filename )
122
+ if ( attachements ) {
123
+ await generateDocument ( row , attachements [ 0 ] , prefix + "-" + filename , outputs )
124
+ }
125
+ }
126
+
127
+ console . log ( "outputs" , outputs )
128
+ if ( outputs . length > 0 ) {
129
+ let existedFilenames :Array < string > = [ ]
130
+ const outputFileName = ! single ? `documents.zip` : ( outputs [ 0 ] as any ) . filename + ".docx"
131
+
132
+ if ( ! single ) {
133
+ for ( let index = 0 ; index < outputs . length ; index ++ ) {
134
+ const docxItem :any = outputs [ index ] ;
135
+ const uniqueFilename = getUniqueFilename ( existedFilenames , docxItem . filename )
136
+ existedFilenames . push ( uniqueFilename )
137
+ outputZip . file ( uniqueFilename + ".docx" , docxItem . content )
138
+ }
139
+ const content = outputZip . generate ( { type : "blob" } )
140
+ saveAs ( content , outputFileName )
141
+ } else {
142
+ const outputZip = new PizZip ( ( outputs [ 0 ] as any ) . content )
143
+ saveAs ( outputZip . generate ( { type : "blob" } ) , outputFileName )
144
+ }
145
+
118
146
}
119
147
120
148
}
121
149
150
+ const getUniqueFilename = ( existedFilenames , newFilename ) => {
151
+ let targetFileName = newFilename
152
+ if ( existedFilenames . indexOf ( newFilename ) > - 1 ) {
153
+ for ( var i = 1 ; i < 9999 ; i ++ ) {
154
+ targetFileName = newFilename + "_" + i
155
+ if ( existedFilenames . indexOf ( targetFileName ) == - 1 ) {
156
+ break ;
157
+ }
158
+ }
159
+ }
160
+ return targetFileName
161
+ }
162
+
122
163
/**
123
164
* Docxtemplater 自定义标签解析器
124
165
* @param tag 标签名称,eg: {产品名称}
@@ -141,10 +182,13 @@ function generateDocuments(selectedRecords: Record[], fields: Field[], selectedA
141
182
get ( scope , context : DXT . ParserContext ) {
142
183
console . log ( { tag, scope, context, TernaryResult } )
143
184
185
+ if ( tag === "." ) {
186
+ return ( typeof scope == "string" ) ? scope : JSON . stringify ( scope )
187
+ }
188
+
144
189
if ( [ "$index" , "$序号" ] . includes ( tag ) ) {
145
190
const indexes = context . scopePathItem
146
191
return indexes [ indexes . length - 1 ] + 1
147
-
148
192
} else if ( tag === "$isLast" ) {
149
193
const totalLength = context . scopePathLength [ context . scopePathLength . length - 1 ]
150
194
const index = context . scopePathItem [ context . scopePathItem . length - 1 ]
@@ -188,52 +232,53 @@ function generateDocuments(selectedRecords: Record[], fields: Field[], selectedA
188
232
/**
189
233
* 调用第三方库,生成word文档并调起浏览器附件下载事件
190
234
*/
191
- function generateDocument ( row : any , selectedAttachment : IAttachmentValue , filename : string ) {
192
-
193
- loadFile ( selectedAttachment . url , function ( error , content ) {
194
- if ( error ) {
195
- throw error
196
- }
197
-
198
- const zip = new PizZip ( content )
199
-
200
- try {
235
+ async function generateDocument ( row : any , selectedAttachment : IAttachmentValue , filename : string , outputs : any ) {
236
+ return new Promise < void > ( ( resolve , reject ) => {
237
+ loadFile ( selectedAttachment . url , function ( error , content : ArrayBuffer ) {
238
+ if ( error ) {
239
+ throw error
240
+ }
201
241
202
- const doc = new Docxtemplater ( zip , {
203
- paragraphLoop : true ,
204
- linebreaks : true ,
205
- parser : customParser ,
206
- } )
242
+ if ( 0 == content . byteLength ) {
243
+ return alert ( "Word模板文件的内容为空,请按照教程语法提前填写。" )
244
+ }
207
245
208
- doc . setData ( { ...row , userGreeting : ( scope ) => {
209
- return "How is it going, " + scope . user + " ? " ;
210
- } } )
246
+ const zip = new PizZip ( content )
211
247
212
248
try {
213
- doc . render ( ) ;
214
- } catch ( error : any ) {
215
- throwError ( error )
216
- }
217
-
218
- const out = doc . getZip ( ) . generate ( {
219
- type : 'blob' ,
220
- mimeType :
221
- 'application/vnd.openxmlformats-officedocument.wordprocessingml.document'
222
- } ) ;
223
249
224
- saveAs ( out , filename + ".docx" )
225
- } catch ( error ) {
226
- console . log ( "错误信息" , error )
227
- alert ( `文件 ${ selectedAttachment . name } 的模板语法不正确,请检查` )
228
- }
250
+ const doc = new Docxtemplater ( zip , {
251
+ paragraphLoop : true ,
252
+ linebreaks : true ,
253
+ parser : customParser ,
254
+ } )
229
255
256
+ try {
257
+ doc . setData ( { ...row } ) . render ( ) ;
258
+ } catch ( error : any ) {
259
+ throwError ( error )
260
+ }
230
261
231
- // await uploadAttachment(activeDatasheetId, out).then(res => {
232
- // console.log(res)
233
- // })
262
+ const out = doc . getZip ( ) . generate ( {
263
+ type : 'arraybuffer' ,
264
+ mimeType :
265
+ 'application/vnd.openxmlformats-officedocument.wordprocessingml.document'
266
+ } ) ;
234
267
268
+ outputs . push ( {
269
+ "content" : out ,
270
+ "filename" : filename
271
+ } )
235
272
236
- } ) ;
273
+ //saveAs(out, filename + ".docx")
274
+ resolve ( )
275
+ } catch ( error ) {
276
+ console . log ( "错误信息" , error )
277
+ alert ( `文件 ${ selectedAttachment . name } 的模板语法不正确,请检查` )
278
+ reject ( )
279
+ }
280
+ } )
281
+ } )
237
282
}
238
283
239
284
/**
@@ -285,6 +330,7 @@ export const DocxGenerator: React.FC = () => {
285
330
const selectedAttachmentField = useField ( fieldId )
286
331
287
332
const [ selectedRecords , setSelectedRecords ] = useState < Record [ ] > ( [ ] )
333
+ const [ processing , setProcessing ] = useState < Boolean > ( false )
288
334
289
335
const recordIds = selectionRecords . map ( ( record : Record ) => {
290
336
return record . recordId
@@ -327,6 +373,14 @@ export const DocxGenerator: React.FC = () => {
327
373
</ a >
328
374
)
329
375
376
+ let btnProps :IButtonProps = {
377
+ variant : "fill" ,
378
+ color : "primary" ,
379
+ size :"small"
380
+ }
381
+
382
+ if ( processing ) btnProps . disabled = true ;
383
+
330
384
return (
331
385
< div style = { style1 } >
332
386
{ helpLink }
@@ -348,7 +402,20 @@ export const DocxGenerator: React.FC = () => {
348
402
</ div >
349
403
350
404
< div style = { { marginTop : '16px' , textAlign : 'center' } } >
351
- { ( selectedRecords . length > 0 ) ? < Button onClick = { ( e ) => generateDocuments ( selectedRecords , fields , selectedAttachmentField , primaryField ) } variant = "fill" color = "primary" size = "small" > 导出 Word 文档</ Button > : "请点击表格任意单元格" }
405
+ {
406
+ ( selectedRecords . length > 0 ) ?
407
+ < Button
408
+ { ...btnProps }
409
+ onClick = { async ( e ) => {
410
+ setProcessing ( true )
411
+ await generateDocuments ( selectedRecords , fields , selectedAttachmentField , primaryField )
412
+ setProcessing ( false )
413
+ } }
414
+ >
415
+ { ! processing ? "导出 Word 文档" : "生成中,请稍候" }
416
+ </ Button > :
417
+ "请点击表格任意单元格"
418
+ }
352
419
</ div >
353
420
</ div >
354
421
}
@@ -362,7 +429,7 @@ export const DocxGenerator: React.FC = () => {
362
429
alignItems : 'center' ,
363
430
width : '100%'
364
431
} } >
365
- < img src = 'https://s1.vika.cn/space/2021/12/29/5a4c225aed81490583cedbecf4bc3419' style = { { width : '30% ' } } />
432
+ < img src = 'https://s1.vika.cn/space/2021/12/29/5a4c225aed81490583cedbecf4bc3419' style = { { width : '48px ' } } />
366
433
</ div >
367
434
< div > 请设置一个存储word模板的附件字段</ div >
368
435
0 commit comments