作者 lixiang

1、增加参会证生成功能

2、增加指定人员参会证下载功能
3、增加批量下载参会证功能
... ... @@ -141,3 +141,25 @@ export function batchUpdateAttendees(data) {
})
}
export function validateBatchUpdate(data) {
return request({
url: '/xvisit/meetingattendance/validateBatchUpdate',
method: 'post',
data: data,
headers: {
'Content-Type': 'multipart/form-data'
}
})
}
export function exportBatchUpdateTemplate(data) {
return request({
url: '/xvisit/meetingattendance/exportBatchUpdateTemplate',
method: 'post',
data: data,
responseType: 'blob'
})
}
... ...
... ... @@ -298,7 +298,27 @@ export default {
download_fail_empty: "ダウンロード内容が空です",
download_cancel: "ダウンロードをキャンセルしました",
badge: "参会証",
batch_download :"一括ダウンロード"
batch_download :"一括ダウンロード",
batch_update: "一括修正",
batch_update_title: "参加者情報の一括修正",
batch_update_tip: "修正してアップロードしてください。テンプレートのスタイルルールなどの情報は修正しないでください。",
download_template: "一括修正テンプレートをダウンロード。",
select_file: "ファイルを選択",
upload_file: "ファイルをアップロード",
upload_tip: "xlsx形式のファイルのみアップロード可能",
select_data_first: "修正するデータを選択してください",
preparing_download: "ダウンロード準備中...",
download_failed: "ダウンロード失敗:",
only_excel: "Excelファイルのみアップロード可能",
batch_update_success: "一括修正が成功しました",
batch_update_failed: "一括修正に失敗しました",
upload_hint: 'ファイルをここにドラッグするか、',
click_to_select: 'クリックしてファイルを選択',
batch_update_errors:'一括更新エラーメッセージ',
error_row:'行番号',
error_message:'エラー原因',
confirm:'確認',
export_errors:'エクスポートエラー情報'
},
tab: {
basic: '基本情報',
... ...
... ... @@ -47,6 +47,19 @@
<el-button type="primary" plain icon="el-icon-user" size="mini" @click="openAddAttendeeDialog"
v-hasPermi="['xvisit:meetingattendance:add']">{{ $t('meetatt.add_attendees') }}</el-button>
</el-col>
<el-col :span="1.5">
<el-button
type="warning"
plain
icon="el-icon-edit"
size="mini"
@click="handleBatchUpdate"
:disabled="multiple"
>{{ $t('meetatt.batch_update') }}</el-button>
</el-col>
</el-row>
<el-table v-loading="loading" :data="meetingattendanceList" @selection-change="handleSelectionChange">
... ... @@ -226,6 +239,97 @@
<el-button @click="cancel">{{ $t('meetatt.cancel') }}</el-button>
</div>
</el-dialog>
<el-dialog
:title="$t('meetatt.batch_update_title')"
:visible.sync="batchUpdateDialogVisible"
width="600px"
custom-class="batch-update-dialog"
>
<div class="dialog-content">
<div class="download-section">
<el-alert
:title="$t('meetatt.batch_update_tip')"
type="info"
:closable="false"
show-icon
>
<el-link
type="primary"
@click="downloadBatchTemplate"
class="download-link"
>
<i class="el-icon-download"></i> {{ $t('meetatt.download_template') }}
</el-link>
</el-alert>
</div>
<div class="upload-section">
<el-upload
class="upload-area"
drag
:action="uploadUrl"
:headers="headers"
:on-success="handleUploadSuccess"
:on-error="handleUploadError"
:before-upload="beforeUpload"
:file-list="fileList"
:auto-upload="true"
:show-file-list="false"
:limit="1"
accept=".xlsx"
>
<i class="el-icon-upload"></i>
<div class="el-upload__text">
{{ $t('meetatt.upload_hint') }}<br>
<em>{{ $t('meetatt.click_to_select') }}</em>
</div>
</el-upload>
</div>
<div v-if="uploadStatus === 'uploading'" class="upload-progress">
<el-progress
:percentage="uploadPercentage"
:status="uploadProgressStatus"
:stroke-width="6"
></el-progress>
</div>
</div>
<div slot="footer" class="dialog-footer">
<el-button @click="batchUpdateDialogVisible = false">
{{ $t('meetatt.cancel') }}
</el-button>
</div>
</el-dialog>
<el-dialog
:title="$t('meetatt.batch_update_errors')"
:visible.sync="showErrorDialog"
width="70%"
>
<el-table :data="errorData" border style="width: 100%">
<el-table-column
:label="$t('meetatt.error_row')"
prop="row"
width="100"
/>
<el-table-column
:label="$t('meetatt.error_message')"
prop="message"
/>
</el-table>
<div slot="footer" class="dialog-footer">
<el-button type="primary" @click="showErrorDialog = false">
{{ $t('meetatt.confirm') }}
</el-button>
<el-button @click="exportErrorData">
{{ $t('meetatt.export_errors') }}
</el-button>
</div>
</el-dialog>
</div>
</template>
... ... @@ -243,7 +347,8 @@ import {
getAttendeesForMeeting,
batchAddAttendees,
getAttendeesStatus,
batchUpdateAttendees
batchUpdateAttendees,
exportBatchUpdateTemplate
} from "@/api/xvisit/meetingattendance";
import { listMeeting } from "@/api/xvisit/meeting";
... ... @@ -253,6 +358,17 @@ export default {
name: "Meetingattendance",
data() {
return {
errorData: [], // 存储错误信息
showErrorDialog: false, // 控制错误对话框显示
uploadStatus: '', // uploading, success, error
uploadPercentage: 0,
uploadProgressStatus: '',
batchUpdateDialogVisible: false,
fileList: [],
uploadUrl: "/dev-api/xvisit/meetingattendance/importBatchUpdateData",
headers: {
Authorization: "Bearer " + this.$store.state.user.token
},
addAttendeeDialogVisible: false,
selectedMeeting: null,
meetingOptions: [],
... ... @@ -350,6 +466,137 @@ export default {
},
methods: {
exportErrorData() {
// 创建 CSV 内容
let csvContent = "行番号、エラーの原因\n";
this.errorData.forEach(item => {
csvContent += `${item.row},${item.message}\n`;
});
// 创建 Blob 对象
const blob = new Blob([csvContent], { type: 'text/csv;charset=utf-8;' });
// 创建下载链接
const link = document.createElement('a');
const url = URL.createObjectURL(blob);
link.href = url;
link.setAttribute('download', '一括更新エラー情報.csv');
document.body.appendChild(link);
// 触发下载
link.click();
// 清理
document.body.removeChild(link);
URL.revokeObjectURL(url);
},
// 批量修改按钮点击事件
handleBatchUpdate() {
if (this.ids.length === 0) {
this.$modal.msgWarning(this.$t('meetatt.select_data_first'));
return;
}
this.batchUpdateDialogVisible = true;
this.resetUploadStatus();
},
// 重置上传状态
resetUploadStatus() {
this.uploadStatus = '';
this.uploadPercentage = 0;
this.uploadProgressStatus = '';
},
// 下载批量修改模板
downloadBatchTemplate() {
const params = {
meetingIds: this.ids,
attendeeIds: this.attendIds
};
this.$modal.loading(this.$t('meetatt.preparing_download'));
exportBatchUpdateTemplate(params).then(response => {
const url = window.URL.createObjectURL(new Blob([response]));
const link = document.createElement('a');
link.href = url;
link.setAttribute('download', `batch_update_template_${new Date().getTime()}.xlsx`);
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
window.URL.revokeObjectURL(url);
this.$modal.closeLoading();
}).catch(error => {
this.$modal.closeLoading();
this.$modal.msgError(this.$t('meetatt.download_failed') + (error.message || error));
});
},
// 上传前校验
beforeUpload(file) {
const isExcel = file.name.endsWith('.xlsx') || file.name.endsWith('.xls');
if (!isExcel) {
this.$modal.msgError(this.$t('meetatt.only_excel'));
return false;
}
// 重置上传状态
this.resetUploadStatus();
this.uploadStatus = 'uploading';
this.uploadProgressStatus = '';
// 模拟上传进度
const interval = setInterval(() => {
if (this.uploadPercentage >= 90) {
clearInterval(interval);
return;
}
this.uploadPercentage += 10;
}, 200);
return true;
},
// 上传成功处理
handleUploadSuccess(response, file, fileList) {
this.uploadPercentage = 100;
this.uploadProgressStatus = 'success';
this.uploadStatus = 'success';
if (response.code === 200) {
// 成功处理
if (response.data && response.data.errors && response.data.errors.length > 0) {
// 部分成功,有错误数据
this.errorData = response.data.errors;
this.showErrorDialog = true;
this.$modal.msgWarning(`成功更新 ${response.data.successCount} 条数据,但有 ${response.data.errors.length} 条数据存在问题`);
} else {
// 全部成功
this.$modal.msgSuccess(response.msg || this.$t('meetatt.batch_update_success'));
this.batchUpdateDialogVisible = false;
}
this.getList(); // 刷新列表
} else {
// 处理失败
this.$modal.msgError(response.msg || this.$t('meetatt.batch_update_failed'));
}
this.fileList = [];
},
// 上传失败处理
handleUploadError(err, file, fileList) {
this.uploadPercentage = 100;
this.uploadProgressStatus = 'exception';
this.uploadStatus = 'error';
let errorMsg = this.$t('meetatt.batch_update_failed');
if (err.message) {
errorMsg += ': ' + err.message;
}
this.$modal.msgError(errorMsg);
},
// 打开添加参会人员对话框
openAddAttendeeDialog() {
... ... @@ -454,7 +701,7 @@ export default {
removeAttendeeIds: toRemoveString
}).then(response => {
if (response.code === 200) {
this.$modal.msgSuccess('参会人员更新成功');
this.$modal.msgSuccess('参加者の更新に成功しました');
this.addAttendeeDialogVisible = false;
this.getList(); // 刷新列表
}
... ... @@ -749,16 +996,16 @@ export default {
attendeeId: item.attendeeId
}));
this.$modal.confirm('确认要为选中的' + params.length + '条数据生成参会证吗?').then(() => {
this.$modal.confirm('選択されていることを確認' + params.length + '条データ生成参会証か?').then(() => {
generateBadges(params).then(res => {
if (res.code === 200) {
this.$modal.msgSuccess("参会证生成成功");
this.$modal.msgSuccess("参会証の生成に成功しました");
this.getList();
} else {
this.$modal.msgError(res.msg);
}
}).catch(error => {
this.$modal.msgError("生成参会证失败:" + error.message);
this.$modal.msgError("参会証の生成に失敗しました:" + error.message);
});
}).catch(() => {
... ... @@ -885,4 +1132,49 @@ export default {
padding: 8px 10px;
border-bottom: 1px solid #f0f0f0;
}
.batch-update-dialog {
border-radius: 8px;
}
.dialog-content {
padding: 0 20px;
}
.download-section {
margin-bottom: 20px;
}
.download-link {
margin-left: 10px;
font-size: 14px;
vertical-align: middle;
}
.upload-section {
margin: 20px 0;
}
.upload-area {
border: 1px dashed #d9d9d9;
border-radius: 6px;
padding: 40px 0;
text-align: center;
background-color: #fafafa;
transition: border-color 0.3s;
}
.upload-area:hover {
border-color: #409EFF;
}
.upload-progress {
margin-top: 20px;
}
.dialog-footer {
text-align: right;
padding: 10px 20px 0;
border-top: 1px solid #e8e8e8;
}
</style>
... ...