作者 lixiang

1、增加参会证生成功能

2、增加指定人员参会证下载功能
3、增加批量下载参会证功能
... ... @@ -66,9 +66,41 @@ export function addMeetinglst(params) {
url: '/xvisit/meetingattendance/addMeetinglst' ,
method: 'post',
data: params,
headers: {
'Content-Type': 'application/x-www-form-urlencoded'
}
})
}
export function generateBadges(params) {
return request({
url: '/xvisit/meetingattendance/generateBadges' ,
method: 'post',
data: params
})
}
export function downloadBadge(params) {
return request({
url: 'xvisit/meetingattendance/downloadBadge',
method: 'get',
params: params,
responseType: 'blob', // 确保设置为blob
headers: {
'Content-Type': 'application/json'
}
});
}
export function downloadBadgeBatch(params) {
return request({
url: 'xvisit/meetingattendance/downloadBadgeBatch',
method: 'post',
data: params,
responseType: 'blob',
headers: {
'Content-Type': 'application/json'
}
});
}
... ...
... ... @@ -227,6 +227,7 @@ export default {
},
meetatt: {
query: '検索',
reset: 'リセット',
add: '追加',
... ... @@ -283,11 +284,22 @@ export default {
memo_P: 'X',
del_flag_P: 'X',
code_P: 'QRコードを入力してください',
zh_pass: "中文参会证",
ja_pass: "日文参会证",
attendee_id_R: '個人IDは空白にできない',
meeting_id_R: '会議IDを空にすることはできません',
generate_badge:'参加証を生成',
download: "参加証をダウンロード",
download_zh: "中国語版",
download_ja: "日本語版",
download_confirm: "参加証をダウンロードしますか?",
download_success: "参加証のダウンロードが成功しました",
download_fail: "ダウンロード失敗:",
download_fail_empty: "ダウンロード内容が空です",
download_cancel: "ダウンロードをキャンセルしました",
badge: "参加証",
batch_download :"一括ダウンロード"
},
tab: {
basic: '基本情報',
... ...
... ... @@ -3,11 +3,11 @@
<el-form :model="queryParams" ref="queryForm" size="small" :inline="true" v-show="showSearch" label-width="150px">
<el-form-item :label="$t('meetatt.meeting_id')" prop="meetingId">
<el-input v-model="queryParams.meetingId" :placeholder="$t('meetatt.meeting_id_P')" clearable
@keyup.enter.native="handleQuery" maxlength="20" />
@keyup.enter.native="handleQuery" maxlength="20" />
</el-form-item>
<el-form-item :label="$t('meetatt.attendee_id')" prop="attendeeId">
<el-input v-model="queryParams.attendeeId" :placeholder="$t('meetatt.attendee_id_P')" clearable
@keyup.enter.native="handleQuery" maxlength="20" />
@keyup.enter.native="handleQuery" maxlength="20" />
</el-form-item>
<el-form-item :label="$t('meetatt.attend_sts')" align="center" prop="attendSts" width="150">
<el-select v-model="queryParams.attendSts" @keyup.enter.native="handleQuery" clearable>
... ... @@ -21,7 +21,7 @@
</el-form-item>
<el-form-item>
<el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery">{{ $t('meetatt.query')
}}</el-button>
}}</el-button>
<el-button icon="el-icon-refresh" size="mini" @click="resetQuery">{{ $t('meetatt.reset') }}</el-button>
</el-form-item>
</el-form>
... ... @@ -29,22 +29,31 @@
<el-row :gutter="10" class="mb8">
<el-col :span="1.5">
<el-button type="primary" plain icon="el-icon-plus" size="mini" @click="handleAdd"
v-hasPermi="['xvisit:meetingattendance:add']">{{ $t('meetatt.add') }}</el-button>
v-hasPermi="['xvisit:meetingattendance:add']">{{ $t('meetatt.add') }}</el-button>
</el-col>
<el-col :span="1.5">
<el-button type="warning" plain icon="el-icon-download" size="mini" @click="handleExport"
v-hasPermi="['xvisit:meetingattendance:export']">{{ $t('meetatt.export') }}</el-button>
v-hasPermi="['xvisit:meetingattendance:export']">{{ $t('meetatt.export') }}</el-button>
</el-col>
<el-col :span="1.5">
<el-button type="success" plain icon="el-icon-tickets" size="mini" @click="handleGenerateBadge"
:disabled="multiple" v-hasPermi="['xvisit:meetingattendance:generate']">{{ $t('meetatt.generate_badge') }}</el-button>
</el-col>
<el-col :span="1.5">
<el-button type="info" plain icon="el-icon-download" size="mini" @click="handleBatchDownload"
:disabled="multiple" v-hasPermi="['xvisit:meetingattendance:downloadZip']">{{ $t('meetatt.batch_download') }}</el-button>
</el-col>
</el-row>
<el-table v-loading="loading" :data="meetingattendanceList">
<el-table v-loading="loading" :data="meetingattendanceList" @selection-change="handleSelectionChange">
<el-table-column type="selection" width="55" align="center" />
<el-table-column :label="$t('meetatt.meeting_id')" align="center" prop="meetingId" />
<el-table-column :label="$t('meetatt.meeting_name')" align="center" prop="meetingName" />
<el-table-column :label="$t('meetatt.attendee_id')" align="center" prop="attendeeId" />
<el-table-column :label="$t('meetatt.attendee_name')" align="center" prop="attendeeName" />
<el-table-column :label="$t('meetatt.seat_info')" align="center" prop="seatInfo" />
<el-table-column :label="$t('meetatt.attend_sts')" align="center" prop="attendSts"><template #default="scope">
<el-table-column :label="$t('meetatt.attend_sts')" align="center" prop="attendSts">
<template #default="scope">
<el-tag v-if="scope.row.attendSts == '0'">{{ $t('meetatt.sign_sts_0') }}</el-tag>
<el-tag v-else-if="scope.row.attendSts == '1'">{{ $t('meetatt.sign_sts_1') }}</el-tag>
</template>
... ... @@ -55,49 +64,94 @@
<el-tag v-else-if="scope.row.paySts == '1'">{{ $t('meetatt.pay_sts_1') }}</el-tag>
</template>
</el-table-column>
<el-table-column :label="$t('meetatt.act')" align="center" class-name="small-padding fixed-width" fixed="right">
<template slot-scope="scope">
<el-button size="mini" type="text" icon="el-icon-edit" @click="handleUpdate(scope.row)"
v-hasPermi="['xvisit:meetingattendance:edit']">{{ $t('meetatt.edit') }}</el-button>
<el-button size="mini" type="text" icon="el-icon-delete" @click="handleDelete(scope.row)"
v-hasPermi="['xvisit:meetingattendance:remove']">{{ $t('meetatt.del') }}</el-button>
</template>
</el-table-column>
<el-table-column :label="$t('meetatt.code')" align="center" prop="attendeeCode" />
<el-table-column :label="$t('meetatt.pay_image')" align="center" prop="payImage" width="100">
<template slot-scope="scope">
<image-preview :src="scope.row.payImage" :width="50" :height="50" />
</template>
</el-table-column>
<el-table-column :label="$t('meetatt.zh_pass')" align="center" width="120">
<template slot-scope="scope">
<div v-if="scope.row.conferencePassZhBase64" class="image-cell">
<el-image
:src="scope.row.conferencePassZhBase64"
:preview-src-list="getPreviewList(scope.row, 'Zh')"
fit="contain"
class="table-image"
:z-index="9999"
@click.stop
>
<div slot="error" class="image-slot">
<i class="el-icon-picture-outline"></i>
</div>
</el-image>
</div>
<span v-else class="no-image">无</span>
</template>
</el-table-column>
<el-table-column :label="$t('meetatt.ja_pass')" align="center" width="120">
<template slot-scope="scope">
<div v-if="scope.row.conferencePassJaBase64" class="image-cell">
<el-image
:src="scope.row.conferencePassJaBase64"
:preview-src-list="getPreviewList(scope.row, 'Ja')"
fit="contain"
class="table-image"
:z-index="9999"
@click.stop
>
<div slot="error" class="image-slot">
<i class="el-icon-picture-outline"></i>
</div>
</el-image>
</div>
<span v-else class="no-image">无</span>
</template>
</el-table-column>
<el-table-column :label="$t('meetatt.act')" align="center" class-name="small-padding fixed-width" fixed="right">
<template slot-scope="scope">
<el-button size="mini" type="text" icon="el-icon-edit" @click="handleUpdate(scope.row)"
v-hasPermi="['xvisit:meetingattendance:edit']">{{ $t('meetatt.edit') }}</el-button>
<el-button size="mini" type="text" icon="el-icon-delete" @click="handleDelete(scope.row)"
v-hasPermi="['xvisit:meetingattendance:remove']">{{ $t('meetatt.del') }}</el-button>
<el-button size="mini" type="text" icon="el-icon-download" @click="handleDownload(scope.row)"
v-hasPermi="['xvisit:meetingattendance:download']">{{ $t('meetatt.download') }}</el-button>
</template>
</el-table-column>
</el-table>
<pagination v-show="total > 0" :total="total" :page.sync="queryParams.pageNum" :limit.sync="queryParams.pageSize"
@pagination="getList" />
@pagination="getList" />
<!-- 添加会议状态对话框 -->
<el-dialog :title="title" :visible.sync="open" width="400px" append-to-body>
<el-form ref="form" :model="form" :rules="rules" label-width="80px">
<el-form-item :label="$t('meetatt.attendee_id')" prop="attendeeId">
<el-select v-model="form.attendeeId" :placeholder="$t('meetatt.attendee_id')" @change="onAttendeeChange"
:disabled="sts === 0 ? false : true">
:disabled="sts === 0 ? false : true">
<el-option v-for="item in postOptions" :key="item.attendeeId" :label="item.attendeeId"
:value="item.attendeeId"></el-option>
:value="item.attendeeId"></el-option>
</el-select>
</el-form-item>
<el-form-item :label="$t('meetatt.attendee_name')" label-width="80px">
<el-input v-model="form.name" :placeholder="$t('meetatt.attendee_name')" style="width: 220px"
disabled></el-input>
disabled></el-input>
</el-form-item>
<el-form-item :label="$t('meetatt.meeting_id')" prop="meetingId">
<el-select v-model="form.meetingId" :placeholder="$t('meetatt.meeting_id')" @change="onMeetingChange"
:disabled="sts === 0 ? false : true">
:disabled="sts === 0 ? false : true">
<el-option v-for="item in postOptions2" :key="item.meetingId" :label="item.meetingId"
:value="item.meetingId"></el-option>
:value="item.meetingId"></el-option>
</el-select>
</el-form-item>
<el-form-item :label="$t('meetatt.meeting_name')" label-width="80px">
<el-input v-model="form.meetingName" :placeholder="$t('meetatt.meeting_name')" style="width: 220px"
disabled></el-input>
disabled></el-input>
</el-form-item>
<el-form-item :label="$t('meetatt.seat_info')" prop="seatInfo">
<el-input v-model="form.seatInfo" :placeholder="$t('meetatt.seat_info')" style="width: 220px" maxlength="20" />
... ... @@ -124,7 +178,6 @@
<el-button @click="cancel">{{ $t('meetatt.cancel') }}</el-button>
</div>
</el-dialog>
</div>
</template>
... ... @@ -136,6 +189,9 @@ import {
addMeetingattendance,
updateMeetingattendance,
checkDataExists,
generateBadges,
downloadBadge,
downloadBadgeBatch
} from "@/api/xvisit/meetingattendance";
import { listMeeting } from "@/api/xvisit/meeting";
... ... @@ -221,8 +277,96 @@ export default {
}
this.getList();
},
methods: {
/** 批量下载参会证按钮操作 */
handleBatchDownload() {
if (this.ids.length === 0) {
this.$modal.msgWarning("请至少选择一条数据");
return;
}
// 检查选中的记录是否都有参会证
const hasNoBadge = this.meetingattendanceList
.filter(item => this.ids.includes(item.meetingId) && this.attendIds.includes(item.attendeeId))
.some(item => !item.conferencePassZhBase64 && !item.conferencePassJaBase64);
if (hasNoBadge) {
this.$modal.confirm('选中的记录中存在未生成参会证的数据,是否继续下载已生成的参会证?').then(() => {
this.downloadSelectedBadges();
}).catch(() => {});
} else {
this.downloadSelectedBadges();
}
},
/** 执行批量下载 */
downloadSelectedBadges() {
const params = this.meetingattendanceList
.filter(item => this.ids.includes(item.meetingId) && this.attendIds.includes(item.attendeeId))
.map(item => ({
meetingId: item.meetingId,
attendeeId: item.attendeeId
}));
this.$modal.loading("正在准备批量下载...");
downloadBadgeBatch(params).then(response => {
const url = window.URL.createObjectURL(new Blob([response]));
const link = document.createElement('a');
link.href = url;
link.setAttribute('download', '参会证批量下载.zip');
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("批量下载失败: " + (error.message || error));
console.error('批量下载失败:', error);
});
},
/** 下载参会证按钮操作 */
handleDownload(row) {
const params = {
meetingId: row.meetingId,
attendeeId: row.attendeeId,
langType: 'zh' // 默认中文,可根据需要修改
};
this.$modal.loading("正在准备下载...");
downloadBadge(params).then(response => {
console.log("response.data",response)
// 创建一个URL对象
const url = window.URL.createObjectURL(new Blob([response]));
// 创建a标签并触发下载
const link = document.createElement('a');
link.href = url;
link.setAttribute('download', '参会证.zip');
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("下载失败: " + (error.message || error));
console.error('下载失败:', error);
});
},
getPreviewList(row, type) {
const image = type === 'Zh' ? row.conferencePassZhBase64 : row.conferencePassJaBase64;
return image ? [image] : [];
},
/** 查询签到状态列表 */
getList() {
this.loading = true;
... ... @@ -233,6 +377,14 @@ export default {
});
},
// 多选框选中数据
handleSelectionChange(selection) {
this.ids = selection.map(item => item.meetingId);
this.attendIds = selection.map(item => item.attendeeId);
this.single = selection.length !== 1;
this.multiple = !selection.length;
},
// 取消按钮
cancel() {
this.open = false;
... ... @@ -268,10 +420,6 @@ export default {
/** 重置按钮操作 */
resetQuery() {
// this.$router.push('/meetingattendance');
// this.$router.replace({ path: this.$route.path, query: {} });
this.resetForm("queryForm");
this.resetForm("form");
... ... @@ -292,10 +440,9 @@ export default {
/** 新增会议按钮操作 */
handleAdd() {
this.reset();
this.queryParams.pageNum = 1;
this.queryParams.pageSize = 1000;
listVisit(this.queryParams).then((response) => {
this.postOptions = response.rows.map((row) => ({
attendeeId: row.attendeeId,
... ... @@ -303,8 +450,8 @@ export default {
}));
this.open = true;
this.title = this.$t('meetatt.add_title');
});
listMeeting(this.queryParams).then((response) => {
this.postOptions2 = response.rows.map((row) => ({
meetingId: row.meetingId,
... ... @@ -314,26 +461,24 @@ export default {
},
onAttendeeChange() {
// 根据选中的attendeeId查找对应的name
const selectedAttendee = this.postOptions.find(
(item) => item.attendeeId === this.form.attendeeId
);
if (selectedAttendee) {
this.form.name = selectedAttendee.name;
} else {
this.form.name = ""; // 如果没有找到,清空文本框
this.form.name = "";
}
},
onMeetingChange() {
// 根据选中的attendeeId查找对应的name
const selectedMeeting = this.postOptions2.find(
(item) => item.meetingId === this.form.meetingId
);
if (selectedMeeting) {
this.form.meetingName = selectedMeeting.meetingName;
} else {
this.form.meetingName = ""; // 如果没有找到,清空文本框
this.form.meetingName = "";
}
},
... ... @@ -375,7 +520,6 @@ export default {
})
.catch((error) => {
console.error("请求失败:", error);
// 处理错误
});
}
}
... ... @@ -407,8 +551,70 @@ export default {
},
`meetingattendance_${new Date().getTime()}.xlsx`
);
}
},
/** 生成参会证按钮操作 */
handleGenerateBadge() {
if (this.ids.length === 0) {
this.$modal.msgWarning("请至少选择一条数据");
return;
}
// 构建参数数组,每个元素包含 meetingId 和 attendeeId
const params = this.meetingattendanceList
.filter(item => this.ids.includes(item.meetingId) && this.attendIds.includes(item.attendeeId))
.map(item => ({
meetingId: item.meetingId,
attendeeId: item.attendeeId
}));
this.$modal.confirm('确认要为选中的' + params.length + '条数据生成参会证吗?').then(() => {
generateBadges(params).then(res => {
if (res.code === 200) {
this.$modal.msgSuccess("参会证生成成功");
} else {
this.$modal.msgError(res.msg);
}
}).catch(error => {
this.$modal.msgError("生成参会证失败:" + error.message);
});
}).catch(() => {
});
}
},
};
</script>
<style>
.image-cell {
display: flex;
justify-content: center;
align-items: center;
height: 60px;
cursor: pointer;
}
.table-image {
width: 50px;
height: 50px;
transition: transform 0.3s;
}
.table-image:hover {
transform: scale(1.1);
}
.image-slot {
display: flex;
justify-content: center;
align-items: center;
width: 100%;
height: 100%;
background: #f5f7fa;
color: #909399;
}
.no-image {
color: #999;
}
</style>
... ...