utils/images/downLoadRemoteFile.js
/**
* 获取 blob 实现不跳转下载
* @param {String} url 目标文件地址
* @return {Promise}
*/
const getBlob = (url) => {
return new Promise(resolve => {
const xhr = new XMLHttpRequest();
xhr.open('GET', url, true);
xhr.responseType = 'blob';
xhr.onload = () => {
if (xhr.status === 200) {
resolve(xhr.response);
}
};
xhr.send();
});
}
/**
* 保存
* @param {Blob} blob
* @param {String} filename 想要保存的文件名称
*/
const saveAs = (blob, filename) => {
if (window.navigator.msSaveOrOpenBlob) {
navigator.msSaveBlob(blob, filename);
} else {
let link = document.createElement('a');
let body = document.querySelector('body');
link.href = window.URL.createObjectURL(blob);
link.download = filename;
// fix Firefox
link.style.display = 'none';
body.appendChild(link);
link.click();
body.removeChild(link);
window.URL.revokeObjectURL(link.href);
}
}
/**
* 下载--获取文件 Blob,然后下载重命名
* @param {String} url 目标文件地址
* @param {String} filename 想要保存的文件名称
*/
const downLoadRemoteFile = (url, filename) => {
getBlob(url).then(blob => {
saveAs(blob, filename);
});
}
export default downLoadRemoteFile;
页面:
<template>
<div class="app-container">
<el-form
:model="queryParams"
ref="queryForm"
size="small"
:inline="true"
v-show="showSearch"
label-width="100px"
>
<el-form-item label="活动名称" prop="actName">
<el-input
v-model="queryParams.actName"
placeholder="请输入活动名称"
clearable
@keyup.enter.native="handleQuery"
/>
</el-form-item>
<el-form-item label="归属区域" prop="officeId">
<!-- <el-input
v-model="queryParams.officeId"
placeholder="请输入归属区域"
clearable
@keyup.enter.native="handleQuery"
/> -->
<TreeSelect
class="treeselect-main"
v-model="queryParams.officeId"
:options="areaBelongOptions"
:normalizer="normalizer"
placeholder="请输入归属区域"
@keyup.enter.native="handleQuery"
/>
</el-form-item>
<el-form-item label="活动创建时间" prop="dateRange">
<el-date-picker
:picker-options="pickerOptions"
v-model="dateRange"
style="width: 300px"
value-format="yyyy-MM-dd HH:mm:ss"
type="datetimerange"
range-separator="-"
start-placeholder="开始时间"
end-placeholder="结束时间"
:default-time="['00:00:00', '23:59:59']"
></el-date-picker>
</el-form-item>
<el-form-item>
<el-button
type="primary"
icon="el-icon-search"
size="mini"
@click="handleQuery"
>搜索</el-button
>
<el-button icon="el-icon-refresh" size="mini" @click="resetQuery"
>重置</el-button
>
</el-form-item>
</el-form>
<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="['edu:activity:add']"
>新增</el-button
>
</el-col>
<el-col :span="1.5">
<el-button
type="success"
plain
icon="el-icon-edit"
size="mini"
:disabled="single"
@click="handleUpdate"
v-hasPermi="['edu:activity:edit']"
>修改</el-button
>
</el-col>
<el-col :span="1.5">
<el-button
type="danger"
plain
icon="el-icon-delete"
size="mini"
:disabled="multiple"
@click="handleDelete"
v-hasPermi="['edu:activity:remove']"
>删除</el-button
>
</el-col>
<!-- <el-col :span="1.5">
<el-button
type="warning"
plain
icon="el-icon-upload2"
size="mini"
@click="uploadQrcode"
v-hasPermi="['edu:activity:export']"
>上传二维码</el-button
>
</el-col> -->
<right-toolbar
:showSearch.sync="showSearch"
@queryTable="getList"
></right-toolbar>
</el-row>
<qrcodeDialog :visible.sync="qrcodeShow" :actList.sync="actList" />
<!-- 表格内容 -->
<div class="table">
<el-table
border
v-loading="loading"
:data="activityList"
@selection-change="handleSelectionChange"
>
<el-table-column align="center" label="活动主图" prop="actImg">
<template v-slot="scope">
<el-image style="width: 60px; height: 40px" :src="scope.row.actImg">
</el-image>
</template>
</el-table-column>
<el-table-column
show-overflow-tooltip
v-for="column in columns"
:key="column.prop"
:label="column.label"
:align="column.align"
:prop="column.prop"
height="200"
>
<template v-if="column.slot" v-slot="scope">
<div class="my-table-td-content">
<slot
:name="column.prop"
:scope="scope.row"
:index="scope.$index"
>
<span>{{ column.slot(scope.row) }}</span>
</slot>
</div>
</template>
</el-table-column>
<el-table-column
align="center"
prop="prop"
label="报名时间"
show-overflow-tooltip
>
<template v-slot="scope">
<span>{{ scope.row.signStart }}~{{ scope.row.signEnd }}</span>
</template>
</el-table-column>
<el-table-column align="center" label="二维码" prop="actImg">
<template v-slot="scope">
<el-image
@click.stop.prevent="clickImage"
style="width: 40px; height: 40px"
:src="scope.row.actQrcode"
:preview-src-list="getPreviewList(scope.row.actQrcode)"
>
</el-image>
</template>
</el-table-column>
<el-table-column
label="操作"
align="center"
class-name="small-padding fixed-width"
>
<template slot-scope="scope">
<el-button
size="mini"
type="text"
icon="el-icon-edit"
@click="handleUpdate(scope.row)"
v-hasPermi="['edu:paper:edit']"
>修改</el-button
>
<el-button
size="mini"
type="text"
icon="el-icon-link"
@click="uploadQrcode(scope.row)"
v-hasPermi="['edu:paper:edit']"
>生成二维码</el-button
>
<el-button
size="mini"
type="text"
icon="el-icon-delete"
@click="handleDelete(scope.row)"
v-hasPermi="['edu:paper:remove']"
>删除</el-button
>
</template>
</el-table-column>
</el-table>
</div>
<div class="code" style="display: none">
<vue-qr
v-if="vueQrStatus"
ref="qrCode"
:text="textValue"
:logoSrc="logoPath"
:logoScale="40"
:size="190"
:margin="10"
:callback="testCallback"
/>
</div>
<pagination
v-show="total > 0"
:total="total"
:page.sync="queryParams.pageNum"
:limit.sync="queryParams.pageSize"
@pagination="getList"
/>
<!-- 添加或修改活动对话框 -->
<addEditActivityDialog
:visible.sync="addEditVisible"
:areaBelongOptions.sync="areaBelongOptions"
:isEdit.sync="isEdit"
:title.sync="title"
:actId.sync="actId"
:selectRow.sync="selectRow"
@getList="getList()"
/>
</div>
</template>
<script>
import {
listActivity,
getActivity,
delActivity,
saveQrcode,
updateActivity,
} from "@/api/edu/activity";
import { listDept } from "@/api/system/dept";
import addEditActivityDialog from "./component/addEditActivityDialog.vue";
import qrcodeDialog from "./component/qrcodeDialog.vue";
import logoImg from "@/assets/logo/logo.png";
import VueQr from "vue-qr";
import { uploadFile2 } from "@/utils/upload-file";
import dayjs from "dayjs";
import downLoadRemoteFile from "@/utils/images/downLoadRemoteFile";
export default {
name: "Activity",
components: {
addEditActivityDialog,
qrcodeDialog,
VueQr,
}, //
data() {
return {
imageDisplay: "",
//选中得id
rowActId: "",
// 控制二维码得生成
vueQrStatus: false,
// 二维码logo
logoPath: logoImg,
// 二维码text
textValue: "",
qrcodeShow: false,
actList: [],
// 活动id
actId: "",
// 增加修改-table某一行row的值
selectRow: {},
// 增加修改-弹出层标题
title: "",
// 增加修改-编辑状态
isEdit: false,
// 归属区域list
areaBelongOptions: [],
// 增加编辑弹出框展示
addEditVisible: false,
// 日期范围
dateRange: [],
// 遮罩层
loading: true,
// 选中数组
ids: [],
// 子表选中数据
checkedEduActivityPaper: [],
// 非单个禁用
single: true,
// 非多个禁用
multiple: true,
// 显示搜索条件
showSearch: true,
// 总条数
total: 0,
// 活动表格数据
activityList: [],
// 活动竞赛试卷表格数据
eduActivityPaperList: [],
// 是否显示弹出层
open: false,
// 查询参数
queryParams: {
pageNum: 1,
pageSize: 10,
actName: null,
officeId: null,
},
// 表单参数
form: {},
// 表单校验
rules: {},
columns: [
{ label: "活动名称", align: "center", prop: "actName" },
// { label: "活动主图", align: "center", prop: "actImg" },
{ label: "活动归属区域", align: "center", prop: "officeName" },
{ label: "状态", align: "center", prop: "statusName" },
{ label: "竞赛时间", align: "center", prop: "competitionTime" },
// { label: "活动海报", align: "center", prop: "actPoster" },
// { label: "活动简介", align: "center", prop: "actMemo" },
],
pickerOptions: {
shortcuts: [
{
text: "最近一周",
onClick(picker) {
const end = new Date();
const start = new Date();
start.setTime(start.getTime() - 3600 * 1000 * 24 * 7);
picker.$emit("pick", [start, end]);
},
},
{
text: "最近一个月",
onClick(picker) {
const end = new Date();
const start = new Date();
start.setTime(start.getTime() - 3600 * 1000 * 24 * 30);
picker.$emit("pick", [start, end]);
},
},
],
},
};
},
created() {
this.getList();
this.getTreeList();
},
methods: {
// 二维码生成得callback
testCallback(dataUrl, id) {
const file = this.base64ToFile(dataUrl, "image.png");
// console.log(file, "dataUrl");
// const reader = new FileReader();
// reader.onload = function (event) {
// document.getElementById("imageDisplay").src = event.target.result;
// };
// reader.readAsDataURL(file);
// return;
console.log(file, "file");
// console.log(dataUrl, id);
const TODAY = dayjs().format("YYYY/MM/DD");
uploadFile2(`/quiz/cover/${TODAY}/`, file, (err, data) => {
if (err) {
this.$modal.msgError(`上传失败:${err}`);
} else {
const imageUrl = `http://${data.Location}`;
console.log(imageUrl, "imageUrl");
const qrcodeUrl = imageUrl;
saveQrcode({ actId: this.rowActId, qrcodeUrl: qrcodeUrl }).then(
(res) => {
if (res.code === 200) {
this.$modal.msgSuccess("生成二维码成功");
this.getList();
// this.open = false;
}
}
);
}
});
},
// 上传二维码
uploadQrcode(row) {
this.rowActId = row.actId;
this.textValue = `http://192.168.2.101:9000/?actId=${row.actId}`;
this.$nextTick(() => {
// 控制二维码得生成;
this.vueQrStatus = true;
});
// console.log(this.qrcodeUrl, this.rowActId, "this.qrcodeUrl");
},
// 转换base64图片方法
base64ToFile(base64Data, filename) {
// 将base64的数据部分提取出来
const parts = base64Data.split(";base64,");
const contentType = parts[0].split(":")[1];
const raw = window.atob(parts[1]);
// 将原始数据转换为Uint8Array
const rawLength = raw.length;
const uInt8Array = new Uint8Array(rawLength);
for (let i = 0; i < rawLength; ++i) {
uInt8Array[i] = raw.charCodeAt(i);
}
// 使用Blob对象创建File对象
const blob = new Blob([uInt8Array], { type: contentType });
blob.lastModifiedDate = new Date();
blob.name = filename;
return new File([blob], filename, { type: contentType });
},
getTreeList() {
listDept().then((response) => {
this.areaBelongOptions = this.handleTree(response.data, "deptId");
});
},
/** 查询活动列表 */
getList() {
this.loading = true;
listActivity(this.addDateRange(this.queryParams, this.dateRange)).then(
(response) => {
this.activityList = response.rows;
this.total = response.total;
this.loading = false;
}
);
},
/** 搜索按钮操作 */
handleQuery() {
this.queryParams.pageNum = 1;
this.getList();
},
/** 重置按钮操作 */
resetQuery() {
// 置空时间区间
this.dateRange = "";
this.resetForm("queryForm");
this.handleQuery();
},
// 多选框选中数据
handleSelectionChange(selection) {
this.ids = selection.map((item) => item.actId);
this.single = selection.length !== 1;
this.multiple = !selection.length;
},
/** 新增按钮操作 */
handleAdd() {
this.addEditVisible = true;
this.title = "添加活动";
// 增加状态
this.isEdit = false;
// this.reset();
// this.open = true;
},
/** 修改按钮操作 */
handleUpdate(row) {
// 打开弹出层
this.addEditVisible = true;
// 编辑状态;
this.isEdit = true;
//传入title
this.title = "修改活动";
// 传入活动id
this.actId = row.actId;
// 获取详情传值子组件
getActivity(row.actId).then((response) => {
this.selectRow = response.data;
});
},
/** 删除按钮操作 */
handleDelete(row) {
const actIds = row.actId;
this.$modal
.confirm('是否确认删除活动编号为"' + actIds + '"的数据项?')
.then(function () {
return delActivity(actIds);
})
.then(() => {
this.getList();
this.$modal.msgSuccess("删除成功");
})
.catch(() => {});
},
/**
* @description: 查看-大图预览, 仅预览当前大图
* @param {String} imgUrl 当前图片URL
* @return {Array} arr 当前图片为第一个的大图
*/
getPreviewList(imgUrl) {
return [imgUrl];
},
/**
* @description: 预览的图片添加下载按钮
*/
clickImage() {
this.$nextTick(() => {
let wrapper = document.getElementsByClassName(
"el-image-viewer__actions__inner"
);
if (wrapper.length > 0) {
let downImg = document.createElement("i");
downImg.setAttribute("class", "el-icon-download");
downImg.style.cursor = "pointer";
wrapper[0].appendChild(downImg);
this.cusClickHandler(downImg);
}
});
},
/**
* @description: 预览的图片给下载按钮添加事件
* @param {HTMLElement} downImg 下载按钮元素
*/
cusClickHandler(downImg) {
downImg.addEventListener("click", () => {
const imgUrl = document.getElementsByClassName(
"el-image-viewer__img"
)[0].src;
const fileName = this.getFileNameFromUrl(imgUrl);
downLoadRemoteFile(imgUrl, fileName);
});
},
/**
* @description: 从 URL 中提取文件名的辅助方法
* @param {String} url 图片的 URL
* @return {String} 文件名
*/
getFileNameFromUrl(url) {
return url.substring(url.lastIndexOf("/") + 1);
},
/** 转换部门数据结构 */
normalizer(node) {
if (node.children && !node.children.length) {
delete node.children;
}
return {
id: node.deptId,
label: node.deptName,
children: node.children,
};
},
},
};
</script>
<style lang="scss">
.treeselect-main {
width: 250px;
line-height: 30px;
.vue-treeselect__placeholder {
line-height: 28px;
}
.vue-treeselect__control {
height: 28px;
}
.vue-treeselect__menu {
border: none;
font-weight: 100;
}
}
.table {
.el-table__cell {
.cell {
white-space: nowrap;
text-overflow: ellipsis;
overflow: hidden;
word-break: break-all;
}
}
}
</style>