|
@@ -47,6 +47,19 @@ |
|
@@ -47,6 +47,19 @@ |
|
47
|
<el-button type="primary" plain icon="el-icon-user" size="mini" @click="openAddAttendeeDialog"
|
47
|
<el-button type="primary" plain icon="el-icon-user" size="mini" @click="openAddAttendeeDialog"
|
|
48
|
v-hasPermi="['xvisit:meetingattendance:add']">{{ $t('meetatt.add_attendees') }}</el-button>
|
48
|
v-hasPermi="['xvisit:meetingattendance:add']">{{ $t('meetatt.add_attendees') }}</el-button>
|
|
49
|
</el-col>
|
49
|
</el-col>
|
|
|
|
50
|
+
|
|
|
|
51
|
+ <el-col :span="1.5">
|
|
|
|
52
|
+ <el-button
|
|
|
|
53
|
+ type="warning"
|
|
|
|
54
|
+ plain
|
|
|
|
55
|
+ icon="el-icon-edit"
|
|
|
|
56
|
+ size="mini"
|
|
|
|
57
|
+ @click="handleBatchUpdate"
|
|
|
|
58
|
+ :disabled="multiple"
|
|
|
|
59
|
+
|
|
|
|
60
|
+ >{{ $t('meetatt.batch_update') }}</el-button>
|
|
|
|
61
|
+ </el-col>
|
|
|
|
62
|
+
|
|
50
|
</el-row>
|
63
|
</el-row>
|
|
51
|
|
64
|
|
|
52
|
<el-table v-loading="loading" :data="meetingattendanceList" @selection-change="handleSelectionChange">
|
65
|
<el-table v-loading="loading" :data="meetingattendanceList" @selection-change="handleSelectionChange">
|
|
@@ -226,6 +239,97 @@ |
|
@@ -226,6 +239,97 @@ |
|
226
|
<el-button @click="cancel">{{ $t('meetatt.cancel') }}</el-button>
|
239
|
<el-button @click="cancel">{{ $t('meetatt.cancel') }}</el-button>
|
|
227
|
</div>
|
240
|
</div>
|
|
228
|
</el-dialog>
|
241
|
</el-dialog>
|
|
|
|
242
|
+
|
|
|
|
243
|
+
|
|
|
|
244
|
+ <el-dialog
|
|
|
|
245
|
+ :title="$t('meetatt.batch_update_title')"
|
|
|
|
246
|
+ :visible.sync="batchUpdateDialogVisible"
|
|
|
|
247
|
+ width="600px"
|
|
|
|
248
|
+ custom-class="batch-update-dialog"
|
|
|
|
249
|
+ >
|
|
|
|
250
|
+ <div class="dialog-content">
|
|
|
|
251
|
+ <div class="download-section">
|
|
|
|
252
|
+ <el-alert
|
|
|
|
253
|
+ :title="$t('meetatt.batch_update_tip')"
|
|
|
|
254
|
+ type="info"
|
|
|
|
255
|
+ :closable="false"
|
|
|
|
256
|
+ show-icon
|
|
|
|
257
|
+ >
|
|
|
|
258
|
+ <el-link
|
|
|
|
259
|
+ type="primary"
|
|
|
|
260
|
+ @click="downloadBatchTemplate"
|
|
|
|
261
|
+ class="download-link"
|
|
|
|
262
|
+ >
|
|
|
|
263
|
+ <i class="el-icon-download"></i> {{ $t('meetatt.download_template') }}
|
|
|
|
264
|
+ </el-link>
|
|
|
|
265
|
+ </el-alert>
|
|
|
|
266
|
+ </div>
|
|
|
|
267
|
+
|
|
|
|
268
|
+ <div class="upload-section">
|
|
|
|
269
|
+ <el-upload
|
|
|
|
270
|
+ class="upload-area"
|
|
|
|
271
|
+ drag
|
|
|
|
272
|
+ :action="uploadUrl"
|
|
|
|
273
|
+ :headers="headers"
|
|
|
|
274
|
+ :on-success="handleUploadSuccess"
|
|
|
|
275
|
+ :on-error="handleUploadError"
|
|
|
|
276
|
+ :before-upload="beforeUpload"
|
|
|
|
277
|
+ :file-list="fileList"
|
|
|
|
278
|
+ :auto-upload="true"
|
|
|
|
279
|
+ :show-file-list="false"
|
|
|
|
280
|
+ :limit="1"
|
|
|
|
281
|
+ accept=".xlsx"
|
|
|
|
282
|
+ >
|
|
|
|
283
|
+ <i class="el-icon-upload"></i>
|
|
|
|
284
|
+ <div class="el-upload__text">
|
|
|
|
285
|
+ {{ $t('meetatt.upload_hint') }}<br>
|
|
|
|
286
|
+ <em>{{ $t('meetatt.click_to_select') }}</em>
|
|
|
|
287
|
+ </div>
|
|
|
|
288
|
+ </el-upload>
|
|
|
|
289
|
+ </div>
|
|
|
|
290
|
+
|
|
|
|
291
|
+ <div v-if="uploadStatus === 'uploading'" class="upload-progress">
|
|
|
|
292
|
+ <el-progress
|
|
|
|
293
|
+ :percentage="uploadPercentage"
|
|
|
|
294
|
+ :status="uploadProgressStatus"
|
|
|
|
295
|
+ :stroke-width="6"
|
|
|
|
296
|
+ ></el-progress>
|
|
|
|
297
|
+ </div>
|
|
|
|
298
|
+ </div>
|
|
|
|
299
|
+
|
|
|
|
300
|
+ <div slot="footer" class="dialog-footer">
|
|
|
|
301
|
+ <el-button @click="batchUpdateDialogVisible = false">
|
|
|
|
302
|
+ {{ $t('meetatt.cancel') }}
|
|
|
|
303
|
+ </el-button>
|
|
|
|
304
|
+ </div>
|
|
|
|
305
|
+ </el-dialog>
|
|
|
|
306
|
+
|
|
|
|
307
|
+
|
|
|
|
308
|
+ <el-dialog
|
|
|
|
309
|
+ :title="$t('meetatt.batch_update_errors')"
|
|
|
|
310
|
+ :visible.sync="showErrorDialog"
|
|
|
|
311
|
+ width="70%"
|
|
|
|
312
|
+ >
|
|
|
|
313
|
+ <el-table :data="errorData" border style="width: 100%">
|
|
|
|
314
|
+ <el-table-column
|
|
|
|
315
|
+ :label="$t('meetatt.error_row')"
|
|
|
|
316
|
+ prop="row"
|
|
|
|
317
|
+ width="100"
|
|
|
|
318
|
+ />
|
|
|
|
319
|
+ <el-table-column
|
|
|
|
320
|
+ :label="$t('meetatt.error_message')"
|
|
|
|
321
|
+ prop="message"
|
|
|
|
322
|
+ />
|
|
|
|
323
|
+ </el-table>
|
|
|
|
324
|
+ <div slot="footer" class="dialog-footer">
|
|
|
|
325
|
+ <el-button type="primary" @click="showErrorDialog = false">
|
|
|
|
326
|
+ {{ $t('meetatt.confirm') }}
|
|
|
|
327
|
+ </el-button>
|
|
|
|
328
|
+ <el-button @click="exportErrorData">
|
|
|
|
329
|
+ {{ $t('meetatt.export_errors') }}
|
|
|
|
330
|
+ </el-button>
|
|
|
|
331
|
+ </div>
|
|
|
|
332
|
+ </el-dialog>
|
|
229
|
</div>
|
333
|
</div>
|
|
230
|
</template>
|
334
|
</template>
|
|
231
|
|
335
|
|
|
@@ -243,7 +347,8 @@ import { |
|
@@ -243,7 +347,8 @@ import { |
|
243
|
getAttendeesForMeeting,
|
347
|
getAttendeesForMeeting,
|
|
244
|
batchAddAttendees,
|
348
|
batchAddAttendees,
|
|
245
|
getAttendeesStatus,
|
349
|
getAttendeesStatus,
|
|
246
|
- batchUpdateAttendees
|
350
|
+ batchUpdateAttendees,
|
|
|
|
351
|
+ exportBatchUpdateTemplate
|
|
247
|
} from "@/api/xvisit/meetingattendance";
|
352
|
} from "@/api/xvisit/meetingattendance";
|
|
248
|
|
353
|
|
|
249
|
import { listMeeting } from "@/api/xvisit/meeting";
|
354
|
import { listMeeting } from "@/api/xvisit/meeting";
|
|
@@ -253,6 +358,17 @@ export default { |
|
@@ -253,6 +358,17 @@ export default { |
|
253
|
name: "Meetingattendance",
|
358
|
name: "Meetingattendance",
|
|
254
|
data() {
|
359
|
data() {
|
|
255
|
return {
|
360
|
return {
|
|
|
|
361
|
+ errorData: [], // 存储错误信息
|
|
|
|
362
|
+ showErrorDialog: false, // 控制错误对话框显示
|
|
|
|
363
|
+ uploadStatus: '', // uploading, success, error
|
|
|
|
364
|
+ uploadPercentage: 0,
|
|
|
|
365
|
+ uploadProgressStatus: '',
|
|
|
|
366
|
+ batchUpdateDialogVisible: false,
|
|
|
|
367
|
+ fileList: [],
|
|
|
|
368
|
+ uploadUrl: "/dev-api/xvisit/meetingattendance/importBatchUpdateData",
|
|
|
|
369
|
+ headers: {
|
|
|
|
370
|
+ Authorization: "Bearer " + this.$store.state.user.token
|
|
|
|
371
|
+ },
|
|
256
|
addAttendeeDialogVisible: false,
|
372
|
addAttendeeDialogVisible: false,
|
|
257
|
selectedMeeting: null,
|
373
|
selectedMeeting: null,
|
|
258
|
meetingOptions: [],
|
374
|
meetingOptions: [],
|
|
@@ -350,6 +466,137 @@ export default { |
|
@@ -350,6 +466,137 @@ export default { |
|
350
|
},
|
466
|
},
|
|
351
|
|
467
|
|
|
352
|
methods: {
|
468
|
methods: {
|
|
|
|
469
|
+ exportErrorData() {
|
|
|
|
470
|
+ // 创建 CSV 内容
|
|
|
|
471
|
+ let csvContent = "行番号、エラーの原因\n";
|
|
|
|
472
|
+ this.errorData.forEach(item => {
|
|
|
|
473
|
+ csvContent += `${item.row},${item.message}\n`;
|
|
|
|
474
|
+ });
|
|
|
|
475
|
+
|
|
|
|
476
|
+ // 创建 Blob 对象
|
|
|
|
477
|
+ const blob = new Blob([csvContent], { type: 'text/csv;charset=utf-8;' });
|
|
|
|
478
|
+
|
|
|
|
479
|
+ // 创建下载链接
|
|
|
|
480
|
+ const link = document.createElement('a');
|
|
|
|
481
|
+ const url = URL.createObjectURL(blob);
|
|
|
|
482
|
+ link.href = url;
|
|
|
|
483
|
+ link.setAttribute('download', '一括更新エラー情報.csv');
|
|
|
|
484
|
+ document.body.appendChild(link);
|
|
|
|
485
|
+
|
|
|
|
486
|
+ // 触发下载
|
|
|
|
487
|
+ link.click();
|
|
|
|
488
|
+
|
|
|
|
489
|
+ // 清理
|
|
|
|
490
|
+ document.body.removeChild(link);
|
|
|
|
491
|
+ URL.revokeObjectURL(url);
|
|
|
|
492
|
+ },
|
|
|
|
493
|
+ // 批量修改按钮点击事件
|
|
|
|
494
|
+ handleBatchUpdate() {
|
|
|
|
495
|
+ if (this.ids.length === 0) {
|
|
|
|
496
|
+ this.$modal.msgWarning(this.$t('meetatt.select_data_first'));
|
|
|
|
497
|
+ return;
|
|
|
|
498
|
+ }
|
|
|
|
499
|
+ this.batchUpdateDialogVisible = true;
|
|
|
|
500
|
+ this.resetUploadStatus();
|
|
|
|
501
|
+ },
|
|
|
|
502
|
+
|
|
|
|
503
|
+ // 重置上传状态
|
|
|
|
504
|
+ resetUploadStatus() {
|
|
|
|
505
|
+ this.uploadStatus = '';
|
|
|
|
506
|
+ this.uploadPercentage = 0;
|
|
|
|
507
|
+ this.uploadProgressStatus = '';
|
|
|
|
508
|
+ },
|
|
|
|
509
|
+
|
|
|
|
510
|
+ // 下载批量修改模板
|
|
|
|
511
|
+ downloadBatchTemplate() {
|
|
|
|
512
|
+ const params = {
|
|
|
|
513
|
+ meetingIds: this.ids,
|
|
|
|
514
|
+ attendeeIds: this.attendIds
|
|
|
|
515
|
+ };
|
|
|
|
516
|
+
|
|
|
|
517
|
+ this.$modal.loading(this.$t('meetatt.preparing_download'));
|
|
|
|
518
|
+
|
|
|
|
519
|
+ exportBatchUpdateTemplate(params).then(response => {
|
|
|
|
520
|
+ const url = window.URL.createObjectURL(new Blob([response]));
|
|
|
|
521
|
+ const link = document.createElement('a');
|
|
|
|
522
|
+ link.href = url;
|
|
|
|
523
|
+ link.setAttribute('download', `batch_update_template_${new Date().getTime()}.xlsx`);
|
|
|
|
524
|
+ document.body.appendChild(link);
|
|
|
|
525
|
+ link.click();
|
|
|
|
526
|
+ document.body.removeChild(link);
|
|
|
|
527
|
+ window.URL.revokeObjectURL(url);
|
|
|
|
528
|
+ this.$modal.closeLoading();
|
|
|
|
529
|
+ }).catch(error => {
|
|
|
|
530
|
+ this.$modal.closeLoading();
|
|
|
|
531
|
+ this.$modal.msgError(this.$t('meetatt.download_failed') + (error.message || error));
|
|
|
|
532
|
+ });
|
|
|
|
533
|
+ },
|
|
|
|
534
|
+
|
|
|
|
535
|
+ // 上传前校验
|
|
|
|
536
|
+ beforeUpload(file) {
|
|
|
|
537
|
+ const isExcel = file.name.endsWith('.xlsx') || file.name.endsWith('.xls');
|
|
|
|
538
|
+ if (!isExcel) {
|
|
|
|
539
|
+ this.$modal.msgError(this.$t('meetatt.only_excel'));
|
|
|
|
540
|
+ return false;
|
|
|
|
541
|
+ }
|
|
|
|
542
|
+
|
|
|
|
543
|
+ // 重置上传状态
|
|
|
|
544
|
+ this.resetUploadStatus();
|
|
|
|
545
|
+ this.uploadStatus = 'uploading';
|
|
|
|
546
|
+ this.uploadProgressStatus = '';
|
|
|
|
547
|
+
|
|
|
|
548
|
+ // 模拟上传进度
|
|
|
|
549
|
+ const interval = setInterval(() => {
|
|
|
|
550
|
+ if (this.uploadPercentage >= 90) {
|
|
|
|
551
|
+ clearInterval(interval);
|
|
|
|
552
|
+ return;
|
|
|
|
553
|
+ }
|
|
|
|
554
|
+ this.uploadPercentage += 10;
|
|
|
|
555
|
+ }, 200);
|
|
|
|
556
|
+
|
|
|
|
557
|
+ return true;
|
|
|
|
558
|
+ },
|
|
|
|
559
|
+
|
|
|
|
560
|
+ // 上传成功处理
|
|
|
|
561
|
+ handleUploadSuccess(response, file, fileList) {
|
|
|
|
562
|
+ this.uploadPercentage = 100;
|
|
|
|
563
|
+ this.uploadProgressStatus = 'success';
|
|
|
|
564
|
+ this.uploadStatus = 'success';
|
|
|
|
565
|
+
|
|
|
|
566
|
+ if (response.code === 200) {
|
|
|
|
567
|
+ // 成功处理
|
|
|
|
568
|
+ if (response.data && response.data.errors && response.data.errors.length > 0) {
|
|
|
|
569
|
+ // 部分成功,有错误数据
|
|
|
|
570
|
+ this.errorData = response.data.errors;
|
|
|
|
571
|
+ this.showErrorDialog = true;
|
|
|
|
572
|
+ this.$modal.msgWarning(`成功更新 ${response.data.successCount} 条数据,但有 ${response.data.errors.length} 条数据存在问题`);
|
|
|
|
573
|
+ } else {
|
|
|
|
574
|
+ // 全部成功
|
|
|
|
575
|
+ this.$modal.msgSuccess(response.msg || this.$t('meetatt.batch_update_success'));
|
|
|
|
576
|
+ this.batchUpdateDialogVisible = false;
|
|
|
|
577
|
+ }
|
|
|
|
578
|
+ this.getList(); // 刷新列表
|
|
|
|
579
|
+ } else {
|
|
|
|
580
|
+ // 处理失败
|
|
|
|
581
|
+ this.$modal.msgError(response.msg || this.$t('meetatt.batch_update_failed'));
|
|
|
|
582
|
+ }
|
|
|
|
583
|
+ this.fileList = [];
|
|
|
|
584
|
+ },
|
|
|
|
585
|
+
|
|
|
|
586
|
+ // 上传失败处理
|
|
|
|
587
|
+ handleUploadError(err, file, fileList) {
|
|
|
|
588
|
+ this.uploadPercentage = 100;
|
|
|
|
589
|
+ this.uploadProgressStatus = 'exception';
|
|
|
|
590
|
+ this.uploadStatus = 'error';
|
|
|
|
591
|
+
|
|
|
|
592
|
+ let errorMsg = this.$t('meetatt.batch_update_failed');
|
|
|
|
593
|
+ if (err.message) {
|
|
|
|
594
|
+ errorMsg += ': ' + err.message;
|
|
|
|
595
|
+ }
|
|
|
|
596
|
+ this.$modal.msgError(errorMsg);
|
|
|
|
597
|
+ },
|
|
|
|
598
|
+
|
|
|
|
599
|
+
|
|
353
|
|
600
|
|
|
354
|
// 打开添加参会人员对话框
|
601
|
// 打开添加参会人员对话框
|
|
355
|
openAddAttendeeDialog() {
|
602
|
openAddAttendeeDialog() {
|
|
@@ -454,7 +701,7 @@ export default { |
|
@@ -454,7 +701,7 @@ export default { |
|
454
|
removeAttendeeIds: toRemoveString
|
701
|
removeAttendeeIds: toRemoveString
|
|
455
|
}).then(response => {
|
702
|
}).then(response => {
|
|
456
|
if (response.code === 200) {
|
703
|
if (response.code === 200) {
|
|
457
|
- this.$modal.msgSuccess('参会人员更新成功');
|
704
|
+ this.$modal.msgSuccess('参加者の更新に成功しました');
|
|
458
|
this.addAttendeeDialogVisible = false;
|
705
|
this.addAttendeeDialogVisible = false;
|
|
459
|
this.getList(); // 刷新列表
|
706
|
this.getList(); // 刷新列表
|
|
460
|
}
|
707
|
}
|
|
@@ -749,16 +996,16 @@ export default { |
|
@@ -749,16 +996,16 @@ export default { |
|
749
|
attendeeId: item.attendeeId
|
996
|
attendeeId: item.attendeeId
|
|
750
|
}));
|
997
|
}));
|
|
751
|
|
998
|
|
|
752
|
- this.$modal.confirm('确认要为选中的' + params.length + '条数据生成参会证吗?').then(() => {
|
999
|
+ this.$modal.confirm('選択されていることを確認' + params.length + '条データ生成参会証か?').then(() => {
|
|
753
|
generateBadges(params).then(res => {
|
1000
|
generateBadges(params).then(res => {
|
|
754
|
if (res.code === 200) {
|
1001
|
if (res.code === 200) {
|
|
755
|
- this.$modal.msgSuccess("参会证生成成功");
|
1002
|
+ this.$modal.msgSuccess("参会証の生成に成功しました");
|
|
756
|
this.getList();
|
1003
|
this.getList();
|
|
757
|
} else {
|
1004
|
} else {
|
|
758
|
this.$modal.msgError(res.msg);
|
1005
|
this.$modal.msgError(res.msg);
|
|
759
|
}
|
1006
|
}
|
|
760
|
}).catch(error => {
|
1007
|
}).catch(error => {
|
|
761
|
- this.$modal.msgError("生成参会证失败:" + error.message);
|
1008
|
+ this.$modal.msgError("参会証の生成に失敗しました:" + error.message);
|
|
762
|
});
|
1009
|
});
|
|
763
|
}).catch(() => {
|
1010
|
}).catch(() => {
|
|
764
|
|
1011
|
|
|
@@ -885,4 +1132,49 @@ export default { |
|
@@ -885,4 +1132,49 @@ export default { |
|
885
|
padding: 8px 10px;
|
1132
|
padding: 8px 10px;
|
|
886
|
border-bottom: 1px solid #f0f0f0;
|
1133
|
border-bottom: 1px solid #f0f0f0;
|
|
887
|
}
|
1134
|
}
|
|
|
|
1135
|
+
|
|
|
|
1136
|
+ .batch-update-dialog {
|
|
|
|
1137
|
+ border-radius: 8px;
|
|
|
|
1138
|
+ }
|
|
|
|
1139
|
+
|
|
|
|
1140
|
+.dialog-content {
|
|
|
|
1141
|
+ padding: 0 20px;
|
|
|
|
1142
|
+}
|
|
|
|
1143
|
+
|
|
|
|
1144
|
+.download-section {
|
|
|
|
1145
|
+ margin-bottom: 20px;
|
|
|
|
1146
|
+}
|
|
|
|
1147
|
+
|
|
|
|
1148
|
+.download-link {
|
|
|
|
1149
|
+ margin-left: 10px;
|
|
|
|
1150
|
+ font-size: 14px;
|
|
|
|
1151
|
+ vertical-align: middle;
|
|
|
|
1152
|
+}
|
|
|
|
1153
|
+
|
|
|
|
1154
|
+.upload-section {
|
|
|
|
1155
|
+ margin: 20px 0;
|
|
|
|
1156
|
+}
|
|
|
|
1157
|
+
|
|
|
|
1158
|
+.upload-area {
|
|
|
|
1159
|
+ border: 1px dashed #d9d9d9;
|
|
|
|
1160
|
+ border-radius: 6px;
|
|
|
|
1161
|
+ padding: 40px 0;
|
|
|
|
1162
|
+ text-align: center;
|
|
|
|
1163
|
+ background-color: #fafafa;
|
|
|
|
1164
|
+ transition: border-color 0.3s;
|
|
|
|
1165
|
+}
|
|
|
|
1166
|
+
|
|
|
|
1167
|
+.upload-area:hover {
|
|
|
|
1168
|
+ border-color: #409EFF;
|
|
|
|
1169
|
+}
|
|
|
|
1170
|
+
|
|
|
|
1171
|
+.upload-progress {
|
|
|
|
1172
|
+ margin-top: 20px;
|
|
|
|
1173
|
+}
|
|
|
|
1174
|
+
|
|
|
|
1175
|
+.dialog-footer {
|
|
|
|
1176
|
+ text-align: right;
|
|
|
|
1177
|
+ padding: 10px 20px 0;
|
|
|
|
1178
|
+ border-top: 1px solid #e8e8e8;
|
|
|
|
1179
|
+}
|
|
888
|
</style> |
1180
|
</style> |