报告查询一键下载
This commit is contained in:
parent
f18a9b53c1
commit
dc7a03c2f8
|
|
@ -100,50 +100,10 @@
|
||||||
<el-form-item>
|
<el-form-item>
|
||||||
<el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery">查询</el-button>
|
<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-button icon="el-icon-refresh" size="mini" @click="resetQuery">重置</el-button>
|
||||||
<el-button
|
<el-button icon="el-icon-download" size="mini" @click="handleBulkDownload">一键下载</el-button>
|
||||||
icon="el-icon-download"
|
|
||||||
size="mini"
|
|
||||||
@click="handleBulkDownload"
|
|
||||||
:disabled="!canDownload"
|
|
||||||
:loading="isExporting">
|
|
||||||
{{ exportButtonText }}
|
|
||||||
</el-button>
|
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</el-form>
|
</el-form>
|
||||||
|
|
||||||
<!-- 进度条显示区域 -->
|
|
||||||
<div v-if="showProgress" class="progress-container">
|
|
||||||
<el-progress
|
|
||||||
:percentage="progressPercentage"
|
|
||||||
:status="progressStatus"
|
|
||||||
:stroke-width="18"
|
|
||||||
:text-inside="true"
|
|
||||||
:show-text="true">
|
|
||||||
</el-progress>
|
|
||||||
<div class="progress-info">
|
|
||||||
<span class="progress-text">{{ progressText }}</span>
|
|
||||||
<span class="progress-detail">{{ currentItem }}/{{ totalItems }} {{ currentFileName }}</span>
|
|
||||||
<span class="progress-speed" v-if="downloadSpeed > 0">
|
|
||||||
速度: {{ formatBytes(downloadSpeed) }}/s
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
<div class="progress-actions" v-if="showProgress">
|
|
||||||
<el-button
|
|
||||||
size="mini"
|
|
||||||
@click="cancelDownload"
|
|
||||||
:disabled="!isExporting"
|
|
||||||
v-if="!downloadComplete">
|
|
||||||
取消下载
|
|
||||||
</el-button>
|
|
||||||
<el-button
|
|
||||||
size="mini"
|
|
||||||
type="success"
|
|
||||||
@click="clearProgress"
|
|
||||||
v-if="downloadComplete">
|
|
||||||
完成
|
|
||||||
</el-button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<el-table v-loading="loading" :data="tableList" ref="multipleTable" row-key="id" @selection-change="handleSelectionChange" border>
|
<el-table v-loading="loading" :data="tableList" ref="multipleTable" row-key="id" @selection-change="handleSelectionChange" border>
|
||||||
<el-table-column type="selection" width="45" :reserve-selection="true"></el-table-column>
|
<el-table-column type="selection" width="45" :reserve-selection="true"></el-table-column>
|
||||||
|
|
@ -341,6 +301,7 @@
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div slot="footer" class="dialog-footer" style="text-align: center">
|
<div slot="footer" class="dialog-footer" style="text-align: center">
|
||||||
|
<!-- <el-button type="primary" @click="handleExportCheck">导出</el-button>-->
|
||||||
<el-button type="primary" @click="printCheck">打 印</el-button>
|
<el-button type="primary" @click="printCheck">打 印</el-button>
|
||||||
<el-button @click="closeDialogAndRefresh">关 闭</el-button>
|
<el-button @click="closeDialogAndRefresh">关 闭</el-button>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -372,9 +333,6 @@ import printJS from "print-js";
|
||||||
import QRCode from "qrcodejs2";
|
import QRCode from "qrcodejs2";
|
||||||
import axios from 'axios'
|
import axios from 'axios'
|
||||||
import {downloadFile} from "@/utils/download";
|
import {downloadFile} from "@/utils/download";
|
||||||
import request from "@/utils/request";
|
|
||||||
import {getToken} from "@/utils/auth";
|
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: "ReportQuery",
|
name: "ReportQuery",
|
||||||
dicts: ['part_task_status'],
|
dicts: ['part_task_status'],
|
||||||
|
|
@ -426,68 +384,23 @@ export default {
|
||||||
rowObj: {},
|
rowObj: {},
|
||||||
|
|
||||||
// 全局选中的项,用于跨页勾选
|
// 全局选中的项,用于跨页勾选
|
||||||
selectedIds: [],
|
selectedItems: new Map(),
|
||||||
selectedData: {}, // 使用对象存储选中项数据,key为id
|
|
||||||
|
|
||||||
// 下载进度相关数据
|
|
||||||
isExporting: false,
|
|
||||||
showProgress: false,
|
|
||||||
downloadComplete: false,
|
|
||||||
progressPercentage: 0,
|
|
||||||
currentItem: 0,
|
|
||||||
totalItems: 0,
|
|
||||||
currentFileName: '',
|
|
||||||
progressText: '准备下载...',
|
|
||||||
progressStatus: '',
|
|
||||||
downloadSpeed: 0,
|
|
||||||
downloadController: null,
|
|
||||||
retryCount: 0,
|
|
||||||
maxRetries: 3,
|
|
||||||
downloadStartTime: 0,
|
|
||||||
downloadedBytes: 0,
|
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
computed: {
|
|
||||||
/** 是否可以下载 */
|
|
||||||
canDownload() {
|
|
||||||
const can = this.selectedIds.length > 0 && !this.isExporting;
|
|
||||||
console.log('canDownload检查:', {
|
|
||||||
selectedIds: this.selectedIds.length,
|
|
||||||
isExporting: this.isExporting,
|
|
||||||
can: can
|
|
||||||
});
|
|
||||||
return can;
|
|
||||||
},
|
|
||||||
|
|
||||||
/** 下载按钮文字 */
|
|
||||||
exportButtonText() {
|
|
||||||
if (this.isExporting) {
|
|
||||||
return `下载中 ${this.progressPercentage}%`;
|
|
||||||
}
|
|
||||||
return `一键下载${this.selectedIds.length > 0 ? ` (${this.selectedIds.length})` : ''}`;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
created() {
|
created() {
|
||||||
console.log('组件初始化');
|
|
||||||
this.getList();
|
this.getList();
|
||||||
this.getImpUnitOptions()
|
this.getImpUnitOptions()
|
||||||
this.departList()
|
this.departList()
|
||||||
this.proList()
|
this.proList()
|
||||||
this.getDeviceType()
|
this.getDeviceType()
|
||||||
},
|
},
|
||||||
beforeDestroy() {
|
|
||||||
// 清理资源
|
|
||||||
if (this.downloadController) {
|
|
||||||
this.downloadController.abort();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
methods: {
|
methods: {
|
||||||
/** 获取分公司下拉 */
|
/** 获取分公司下拉 */
|
||||||
async getImpUnitOptions() {
|
async getImpUnitOptions() {
|
||||||
try {
|
try {
|
||||||
const res = await getImpUnitListApi()
|
const res = await getImpUnitListApi() // 调后台接口
|
||||||
this.impUnitOptions = res.data.map(item => ({
|
this.impUnitOptions = res.data.map(item => ({
|
||||||
label: item.impUnitName,
|
label: item.impUnitName, // 这里根据实际字段替换
|
||||||
value: item.impUnitName
|
value: item.impUnitName
|
||||||
}))
|
}))
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
|
@ -497,9 +410,9 @@ export default {
|
||||||
/** 获取项目部下拉 */
|
/** 获取项目部下拉 */
|
||||||
async departList() {
|
async departList() {
|
||||||
try {
|
try {
|
||||||
const res = await getDepartListByImpUnitApi()
|
const res = await getDepartListByImpUnitApi() // 调后台接口
|
||||||
this.departOptions = res.data.map(item => ({
|
this.departOptions = res.data.map(item => ({
|
||||||
label: item.departName,
|
label: item.departName, // 这里根据实际字段替换
|
||||||
value: item.departName
|
value: item.departName
|
||||||
}))
|
}))
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
|
@ -509,9 +422,9 @@ export default {
|
||||||
/** 获取工程下拉 */
|
/** 获取工程下拉 */
|
||||||
async proList() {
|
async proList() {
|
||||||
try {
|
try {
|
||||||
const res = await getProListByDepartApi()
|
const res = await getProListByDepartApi() // 调后台接口
|
||||||
this.proOptions = res.data.map(item => ({
|
this.proOptions = res.data.map(item => ({
|
||||||
label: item.proName,
|
label: item.proName, // 这里根据实际字段替换
|
||||||
value: item.proName
|
value: item.proName
|
||||||
}))
|
}))
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
|
@ -520,22 +433,22 @@ export default {
|
||||||
},
|
},
|
||||||
/** 分公司选择变化,加载项目部 */
|
/** 分公司选择变化,加载项目部 */
|
||||||
async handleImpUnitChange() {
|
async handleImpUnitChange() {
|
||||||
this.queryParams.departName = null
|
this.queryParams.departName = null // 清空项目部已选
|
||||||
this.departOptions = []
|
this.departOptions = [] // 清空原有下拉
|
||||||
this.queryParams.proName = null
|
this.queryParams.proName = null // 清空工程已选
|
||||||
this.proOptions = []
|
this.proOptions = [] // 清空原有下拉
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const params = {
|
const params = {
|
||||||
impUnitName: this.queryParams.impUnitName,
|
impUnitName: this.queryParams.impUnitName, // 分公司名称
|
||||||
departName: this.queryParams.departName,
|
departName: this.queryParams.departName, // 项目部名称
|
||||||
proName: this.queryParams.proName,
|
proName: this.queryParams.proName,
|
||||||
teamName:this.queryParams.teamName,
|
teamName:this.queryParams.teamName,
|
||||||
subUnitName:this.queryParams.subUnitName,
|
subUnitName:this.queryParams.subUnitName,
|
||||||
}
|
}
|
||||||
const res = await getDepartListByImpUnitApi(params)
|
const res = await getDepartListByImpUnitApi(params)
|
||||||
this.departOptions = res.data.map(item => ({
|
this.departOptions = res.data.map(item => ({
|
||||||
label: item.departName,
|
label: item.departName, // 项目部名称字段
|
||||||
value: item.departName
|
value: item.departName
|
||||||
}))
|
}))
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
|
@ -544,20 +457,21 @@ export default {
|
||||||
},
|
},
|
||||||
/** 项目部选择变化,加载工程 */
|
/** 项目部选择变化,加载工程 */
|
||||||
async handleDepartChange() {
|
async handleDepartChange() {
|
||||||
this.queryParams.proName = null
|
this.queryParams.proName = null // 清空工程已选
|
||||||
this.proOptions = []
|
this.proOptions = [] // 清空原有下拉
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
// 同时传入分公司和项目部参数
|
||||||
const params = {
|
const params = {
|
||||||
impUnitName: this.queryParams.impUnitName,
|
impUnitName: this.queryParams.impUnitName, // 分公司名称
|
||||||
departName: this.queryParams.departName,
|
departName: this.queryParams.departName, // 项目部名称
|
||||||
proName: this.queryParams.proName,
|
proName: this.queryParams.proName,
|
||||||
teamName:this.queryParams.teamName,
|
teamName:this.queryParams.teamName,
|
||||||
subUnitName:this.queryParams.subUnitName,
|
subUnitName:this.queryParams.subUnitName,
|
||||||
}
|
}
|
||||||
const res = await getProListByDepartApi(params)
|
const res = await getProListByDepartApi(params)
|
||||||
this.proOptions = res.data.map(item => ({
|
this.proOptions = res.data.map(item => ({
|
||||||
label: item.proName,
|
label: item.proName, // 工程名称字段
|
||||||
value: item.proName
|
value: item.proName
|
||||||
}))
|
}))
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
|
@ -594,6 +508,7 @@ export default {
|
||||||
} else {
|
} else {
|
||||||
typeId = this.materialNameList.find(item => item.label == e).value
|
typeId = this.materialNameList.find(item => item.label == e).value
|
||||||
}
|
}
|
||||||
|
console.log('🚀 ~ handleMaModel ~ typeId:', typeId)
|
||||||
getDeviceType({ level: 4, skipPermission: 1, typeId }).then(response => {
|
getDeviceType({ level: 4, skipPermission: 1, typeId }).then(response => {
|
||||||
let matModelRes = response.data
|
let matModelRes = response.data
|
||||||
this.materialModelList = matModelRes.map((item) => {
|
this.materialModelList = matModelRes.map((item) => {
|
||||||
|
|
@ -620,11 +535,11 @@ export default {
|
||||||
this.total = response.data.total;
|
this.total = response.data.total;
|
||||||
this.loading = false;
|
this.loading = false;
|
||||||
|
|
||||||
|
// 加载完成后,根据全局选中状态设置当前页的选中项
|
||||||
this.$nextTick(() => {
|
this.$nextTick(() => {
|
||||||
if (this.$refs.multipleTable) {
|
if (this.$refs.multipleTable) {
|
||||||
// 恢复之前选中的行
|
|
||||||
this.tableList.forEach(row => {
|
this.tableList.forEach(row => {
|
||||||
if (this.selectedIds.includes(row.id)) {
|
if (this.selectedItems.has(row.id)) {
|
||||||
this.$refs.multipleTable.toggleRowSelection(row, true);
|
this.$refs.multipleTable.toggleRowSelection(row, true);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
@ -637,8 +552,8 @@ export default {
|
||||||
this.resetForm("queryForm");
|
this.resetForm("queryForm");
|
||||||
this.dateRange=[]
|
this.dateRange=[]
|
||||||
this.queryParams.keyWord=null;
|
this.queryParams.keyWord=null;
|
||||||
this.selectedIds = [];
|
// 清空选中状态
|
||||||
this.selectedData = {};
|
this.selectedItems.clear();
|
||||||
if (this.$refs.multipleTable) {
|
if (this.$refs.multipleTable) {
|
||||||
this.$refs.multipleTable.clearSelection();
|
this.$refs.multipleTable.clearSelection();
|
||||||
}
|
}
|
||||||
|
|
@ -647,38 +562,40 @@ export default {
|
||||||
/** 搜索按钮操作 */
|
/** 搜索按钮操作 */
|
||||||
handleQuery() {
|
handleQuery() {
|
||||||
this.queryParams.pageNum = 1;
|
this.queryParams.pageNum = 1;
|
||||||
this.selectedIds = [];
|
// 查询时清除选中状态
|
||||||
this.selectedData = {};
|
this.selectedItems.clear();
|
||||||
if (this.$refs.multipleTable) {
|
if (this.$refs.multipleTable) {
|
||||||
this.$refs.multipleTable.clearSelection();
|
this.$refs.multipleTable.clearSelection();
|
||||||
}
|
}
|
||||||
this.getList();
|
this.getList();
|
||||||
},
|
},
|
||||||
// ==== 修复关键:简化的选中状态处理 ====
|
// 多选框选中数据
|
||||||
handleSelectionChange(selection) {
|
handleSelectionChange(selection) {
|
||||||
console.log('选中项变化:', selection.length, '项');
|
// 更新全局选中状态
|
||||||
|
|
||||||
// 清空现有选中状态
|
|
||||||
this.selectedIds = [];
|
|
||||||
this.selectedData = {};
|
|
||||||
|
|
||||||
// 重新构建选中状态
|
|
||||||
selection.forEach(item => {
|
selection.forEach(item => {
|
||||||
this.selectedIds.push(item.id);
|
this.selectedItems.set(item.id, item);
|
||||||
this.selectedData[item.id] = item;
|
|
||||||
});
|
});
|
||||||
|
|
||||||
console.log('当前选中ID:', this.selectedIds);
|
// 找出当前页未被选中但之前被选中的项,并从全局选中状态中移除
|
||||||
console.log('选中数据:', this.selectedData);
|
const currentIds = selection.map(item => item.id);
|
||||||
|
for (let id of this.selectedItems.keys()) {
|
||||||
|
if (!currentIds.includes(id)) {
|
||||||
|
const itemInCurrentPage = this.tableList.find(row => row.id === id);
|
||||||
|
if (itemInCurrentPage) {
|
||||||
|
this.selectedItems.delete(id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// 更新其他相关数据
|
// 更新ids和infos数组,用于其他操作
|
||||||
this.ids = this.selectedIds;
|
this.ids = Array.from(this.selectedItems.keys());
|
||||||
this.infos = selection.map(item => ({id: item.id}));
|
this.infos = Array.from(this.selectedItems.values()).map(item => ({ id: item.id }));
|
||||||
this.single = this.ids.length !== 1;
|
this.single = this.ids.length !== 1;
|
||||||
this.multiple = this.ids.length === 0;
|
this.multiple = this.ids.length === 0;
|
||||||
},
|
},
|
||||||
//查看
|
//查看
|
||||||
handleView(row){
|
handleView(row){
|
||||||
|
console.log(row)
|
||||||
let query = { Id:row.id,taskId: row.taskId,isView:"true" }
|
let query = { Id:row.id,taskId: row.taskId,isView:"true" }
|
||||||
this.$tab.closeOpenPage({
|
this.$tab.closeOpenPage({
|
||||||
path: '/part/partAcceptDetail',
|
path: '/part/partAcceptDetail',
|
||||||
|
|
@ -689,18 +606,25 @@ export default {
|
||||||
handleFileView(url) {
|
handleFileView(url) {
|
||||||
if (!url) return;
|
if (!url) return;
|
||||||
|
|
||||||
|
// 获取文件后缀名(忽略大小写)
|
||||||
const fileExt = url.split('.').pop().toLowerCase();
|
const fileExt = url.split('.').pop().toLowerCase();
|
||||||
|
|
||||||
|
// 定义不同类型的文件后缀
|
||||||
const imgExts = ['jpg', 'jpeg', 'png', 'gif', 'bmp', 'webp'];
|
const imgExts = ['jpg', 'jpeg', 'png', 'gif', 'bmp', 'webp'];
|
||||||
const pdfExts = ['pdf'];
|
const pdfExts = ['pdf'];
|
||||||
const docExts = ['doc', 'docx', 'xls', 'xlsx'];
|
const docExts = ['doc', 'docx', 'xls', 'xlsx'];
|
||||||
|
|
||||||
if (imgExts.includes(fileExt)) {
|
if (imgExts.includes(fileExt)) {
|
||||||
|
// 图片预览
|
||||||
this.$viewerApi ? this.$viewerApi({ images: [url] }) : window.open(url);
|
this.$viewerApi ? this.$viewerApi({ images: [url] }) : window.open(url);
|
||||||
} else if (pdfExts.includes(fileExt)) {
|
} else if (pdfExts.includes(fileExt)) {
|
||||||
|
// PDF 预览
|
||||||
window.open(url);
|
window.open(url);
|
||||||
} else if (docExts.includes(fileExt)) {
|
} else if (docExts.includes(fileExt)) {
|
||||||
|
// Word、Excel 文件 → 下载
|
||||||
this.downloadFile(url);
|
this.downloadFile(url);
|
||||||
} else {
|
} else {
|
||||||
|
// 其他文件,默认下载
|
||||||
this.downloadFile(url);
|
this.downloadFile(url);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
@ -745,6 +669,13 @@ export default {
|
||||||
},
|
},
|
||||||
//出库检验单打印
|
//出库检验单打印
|
||||||
printCheck() {
|
printCheck() {
|
||||||
|
// this.$refs.remarksPrintRefCheck.print()
|
||||||
|
// printJS({
|
||||||
|
// printable: 'checkId1',
|
||||||
|
// type: 'html',
|
||||||
|
// targetStyles: ['*']
|
||||||
|
// // 其他配置选项
|
||||||
|
// })
|
||||||
this.$nextTick(() => {
|
this.$nextTick(() => {
|
||||||
printJS({
|
printJS({
|
||||||
printable: 'checkId1',
|
printable: 'checkId1',
|
||||||
|
|
@ -767,32 +698,40 @@ export default {
|
||||||
let context = canvas.getContext('2d')
|
let context = canvas.getContext('2d')
|
||||||
canvas.width = canvas.width
|
canvas.width = canvas.width
|
||||||
context.height = canvas.height
|
context.height = canvas.height
|
||||||
|
// // 清除画布内容
|
||||||
|
// context.clearRect(0, 0, canvas.width, canvas.height);
|
||||||
|
//let text = "XXX专用章";
|
||||||
|
//let companyName = "XXX科技股份有限公司";
|
||||||
|
|
||||||
|
// 绘制印章边框
|
||||||
let width = canvas.width / 2
|
let width = canvas.width / 2
|
||||||
let height = canvas.height / 2
|
let height = canvas.height / 2
|
||||||
context.lineWidth = 3
|
context.lineWidth = 3
|
||||||
context.strokeStyle = '#f00'
|
context.strokeStyle = '#f00'
|
||||||
context.beginPath()
|
context.beginPath()
|
||||||
context.arc(width, height, 80, 0, Math.PI * 2)
|
context.arc(width, height, 80, 0, Math.PI * 2) //宽、高、半径
|
||||||
context.stroke()
|
context.stroke()
|
||||||
|
|
||||||
|
//画五角星
|
||||||
this.create5star(context, width, height, 20, '#f00', 0)
|
this.create5star(context, width, height, 20, '#f00', 0)
|
||||||
|
|
||||||
|
// 绘制印章名称
|
||||||
context.font = '100 13px 宋体'
|
context.font = '100 13px 宋体'
|
||||||
context.textBaseline = 'middle'
|
context.textBaseline = 'middle' //设置文本的垂直对齐方式
|
||||||
context.textAlign = 'center'
|
context.textAlign = 'center' //设置文本的水平对对齐方式
|
||||||
context.lineWidth = 1
|
context.lineWidth = 1
|
||||||
context.strokeStyle = '#ff2f2f'
|
context.strokeStyle = '#ff2f2f'
|
||||||
context.strokeText(text, width, height + 50)
|
context.strokeText(text, width, height + 50)
|
||||||
|
|
||||||
context.translate(width, height)
|
// 绘制印章单位
|
||||||
|
context.translate(width, height) // 平移到此位置,
|
||||||
context.font = '100 13px 宋体'
|
context.font = '100 13px 宋体'
|
||||||
let count = companyName.length
|
let count = companyName.length // 字数
|
||||||
let angle = (4 * Math.PI) / (3 * (count - 1))
|
let angle = (4 * Math.PI) / (3 * (count - 1)) // 字间角度
|
||||||
let chars = companyName.split('')
|
let chars = companyName.split('')
|
||||||
let c
|
let c
|
||||||
for (let i = 0; i < count; i++) {
|
for (let i = 0; i < count; i++) {
|
||||||
c = chars[i]
|
c = chars[i] // 需要绘制的字符
|
||||||
if (i == 0) {
|
if (i == 0) {
|
||||||
context.rotate((5 * Math.PI) / 6)
|
context.rotate((5 * Math.PI) / 6)
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -800,10 +739,10 @@ export default {
|
||||||
}
|
}
|
||||||
|
|
||||||
context.save()
|
context.save()
|
||||||
context.translate(65, 0)
|
context.translate(65, 0) // 平移到此位置,此时字和x轴垂直,公司名称和最外圈的距离
|
||||||
context.rotate(Math.PI / 2)
|
context.rotate(Math.PI / 2) // 旋转90度,让字平行于x轴
|
||||||
context.strokeStyle = '#ff5050'
|
context.strokeStyle = '#ff5050' // 设置印章单位字体颜色为较浅的红色
|
||||||
context.strokeText(c, 0, 0)
|
context.strokeText(c, 0, 0) // 此点为字的中心点
|
||||||
context.restore()
|
context.restore()
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
@ -811,11 +750,14 @@ export default {
|
||||||
create5star(context, sx, sy, radius, color, rotato) {
|
create5star(context, sx, sy, radius, color, rotato) {
|
||||||
context.save()
|
context.save()
|
||||||
context.fillStyle = color
|
context.fillStyle = color
|
||||||
context.translate(sx, sy)
|
context.translate(sx, sy) //移动坐标原点
|
||||||
context.rotate(Math.PI + rotato)
|
context.rotate(Math.PI + rotato) //旋转
|
||||||
context.beginPath()
|
context.beginPath() //创建路径
|
||||||
|
// let x = Math.sin(0);
|
||||||
|
// let y = Math.cos(0);
|
||||||
let dig = (Math.PI / 5) * 4
|
let dig = (Math.PI / 5) * 4
|
||||||
for (let i = 0; i < 5; i++) {
|
for (let i = 0; i < 5; i++) {
|
||||||
|
//画五角星的五条边
|
||||||
let x = Math.sin(i * dig)
|
let x = Math.sin(i * dig)
|
||||||
let y = Math.cos(i * dig)
|
let y = Math.cos(i * dig)
|
||||||
context.lineTo(x * radius, y * radius)
|
context.lineTo(x * radius, y * radius)
|
||||||
|
|
@ -827,14 +769,16 @@ export default {
|
||||||
},
|
},
|
||||||
// 二维码查看
|
// 二维码查看
|
||||||
handleViewQrCode(row) {
|
handleViewQrCode(row) {
|
||||||
|
console.log('🚀 ~ handleViewQrCode ~ row:', row)
|
||||||
this.rowObj = row
|
this.rowObj = row
|
||||||
this.uploadOpen = true
|
this.uploadOpen = true
|
||||||
this.qrCode = row.qrCode
|
this.qrCode = row.qrCode
|
||||||
let str = 'http://ahjj.jypxks.com:9988/imw/backstage/machine/qrCodePage?qrcode=' + row.qrCode
|
let str = 'http://ahjj.jypxks.com:9988/imw/backstage/machine/qrCodePage?qrcode=' + row.qrCode
|
||||||
|
console.log('🚀 ~ handleViewQrCode ~ str:', str)
|
||||||
this.$nextTick(() => {
|
this.$nextTick(() => {
|
||||||
this.$refs.codeItem.innerHTML = ''
|
this.$refs.codeItem.innerHTML = ''
|
||||||
var qrcode = new QRCode(this.$refs.codeItem, {
|
var qrcode = new QRCode(this.$refs.codeItem, {
|
||||||
text: str,
|
text: str, //二维码内容
|
||||||
width: 256,
|
width: 256,
|
||||||
height: 256,
|
height: 256,
|
||||||
colorDark: '#000000',
|
colorDark: '#000000',
|
||||||
|
|
@ -848,9 +792,9 @@ export default {
|
||||||
const qrContainer = document.createElement('div')
|
const qrContainer = document.createElement('div')
|
||||||
document.body.appendChild(qrContainer)
|
document.body.appendChild(qrContainer)
|
||||||
|
|
||||||
const qrSize = 512
|
const qrSize = 512 // 放大二维码
|
||||||
const padding = 20
|
const padding = 20 // 边距也放大
|
||||||
const fontSize = 68
|
const fontSize = 68 // 放大文字
|
||||||
const maxTextWidth = qrSize
|
const maxTextWidth = qrSize
|
||||||
|
|
||||||
const qrcode = new QRCode(qrContainer, {
|
const qrcode = new QRCode(qrContainer, {
|
||||||
|
|
@ -866,6 +810,7 @@ export default {
|
||||||
const img = qrContainer.querySelector('img') || qrContainer.querySelector('canvas')
|
const img = qrContainer.querySelector('img') || qrContainer.querySelector('canvas')
|
||||||
const text = row.maCode || ''
|
const text = row.maCode || ''
|
||||||
|
|
||||||
|
// 计算换行
|
||||||
const ctxMeasure = document.createElement('canvas').getContext('2d')
|
const ctxMeasure = document.createElement('canvas').getContext('2d')
|
||||||
ctxMeasure.font = `${fontSize}px Arial`
|
ctxMeasure.font = `${fontSize}px Arial`
|
||||||
const words = text.split('')
|
const words = text.split('')
|
||||||
|
|
@ -882,6 +827,7 @@ export default {
|
||||||
}
|
}
|
||||||
lines.push(line)
|
lines.push(line)
|
||||||
|
|
||||||
|
// 动态计算画布高度
|
||||||
const lineHeight = fontSize + 10
|
const lineHeight = fontSize + 10
|
||||||
const qrCanvas = document.createElement('canvas')
|
const qrCanvas = document.createElement('canvas')
|
||||||
qrCanvas.width = qrSize + padding * 2
|
qrCanvas.width = qrSize + padding * 2
|
||||||
|
|
@ -893,6 +839,7 @@ export default {
|
||||||
|
|
||||||
ctx.drawImage(img, padding, padding, qrSize, qrSize)
|
ctx.drawImage(img, padding, padding, qrSize, qrSize)
|
||||||
|
|
||||||
|
// 绘制文字
|
||||||
ctx.fillStyle = '#000'
|
ctx.fillStyle = '#000'
|
||||||
ctx.font = `${fontSize}px Arial`
|
ctx.font = `${fontSize}px Arial`
|
||||||
ctx.textAlign = 'center'
|
ctx.textAlign = 'center'
|
||||||
|
|
@ -900,6 +847,7 @@ export default {
|
||||||
ctx.fillText(ln, qrCanvas.width / 2, qrSize + padding + fontSize / 1.2 + index * lineHeight)
|
ctx.fillText(ln, qrCanvas.width / 2, qrSize + padding + fontSize / 1.2 + index * lineHeight)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// 下载
|
||||||
const a = document.createElement('a')
|
const a = document.createElement('a')
|
||||||
a.href = qrCanvas.toDataURL('image/png')
|
a.href = qrCanvas.toDataURL('image/png')
|
||||||
a.download = text + '.png'
|
a.download = text + '.png'
|
||||||
|
|
@ -910,6 +858,7 @@ export default {
|
||||||
},
|
},
|
||||||
/** 单条下载 */
|
/** 单条下载 */
|
||||||
handleDownload(row) {
|
handleDownload(row) {
|
||||||
|
// 构建要传给后端的完整数据结构
|
||||||
const payload = {
|
const payload = {
|
||||||
items: [
|
items: [
|
||||||
{
|
{
|
||||||
|
|
@ -937,38 +886,28 @@ export default {
|
||||||
zipName: row.proName || 'report',
|
zipName: row.proName || 'report',
|
||||||
};
|
};
|
||||||
|
|
||||||
this.downloadSingleFile(payload, `${row.proName || '文件档案下载'}.zip`);
|
this.downloadZip(
|
||||||
|
'/material/bm_report/downloadSingle',
|
||||||
|
JSON.stringify(payload),
|
||||||
|
`${row.proName || '文件档案下载'}.zip`
|
||||||
|
);
|
||||||
},
|
},
|
||||||
|
|
||||||
/** 一键下载(多选) */
|
|
||||||
|
/**
|
||||||
|
* 一键下载(多选)
|
||||||
|
*/
|
||||||
async handleBulkDownload() {
|
async handleBulkDownload() {
|
||||||
|
const grouped = {};
|
||||||
|
try {
|
||||||
// 检查是否有选中项
|
// 检查是否有选中项
|
||||||
if (this.selectedIds.length === 0) {
|
if (!this.selectedItems || this.selectedItems.size === 0) {
|
||||||
this.$message.warning('请先勾选要下载的行');
|
this.$message.warning('请先勾选要下载的行');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log('开始下载,选中数量:', this.selectedIds.length);
|
// 从全局选中状态构建 items,不再只依赖当前页的 tableList
|
||||||
|
const items = Array.from(this.selectedItems.values()).map(item => ({
|
||||||
// 确认对话框
|
|
||||||
try {
|
|
||||||
await this.$confirm(`确定要下载 ${this.selectedIds.length} 个选中项吗?`, '提示', {
|
|
||||||
confirmButtonText: '确定',
|
|
||||||
cancelButtonText: '取消',
|
|
||||||
type: 'warning',
|
|
||||||
center: true
|
|
||||||
});
|
|
||||||
} catch (cancel) {
|
|
||||||
console.log('用户取消下载');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 初始化下载状态
|
|
||||||
this.initializeDownload();
|
|
||||||
|
|
||||||
try {
|
|
||||||
// 构建下载数据 - 直接从selectedData获取
|
|
||||||
const items = Object.values(this.selectedData).map(item => ({
|
|
||||||
proName: item.proName || '',
|
proName: item.proName || '',
|
||||||
departName: item.departName || '',
|
departName: item.departName || '',
|
||||||
typeName: item.typeName || '',
|
typeName: item.typeName || '',
|
||||||
|
|
@ -990,254 +929,54 @@ export default {
|
||||||
remark: ''
|
remark: ''
|
||||||
}));
|
}));
|
||||||
|
|
||||||
console.log('构建了', items.length, '个下载项');
|
// 按工程 -> 领用日期 -> 类型-规格分组
|
||||||
|
items.forEach(item => {
|
||||||
|
const proName = item.proName || "";
|
||||||
|
const leaseDate = item.testTime || "";
|
||||||
|
const typeFolder = `${item.typeName || ""}-${item.typeModelName || ""}`;
|
||||||
|
|
||||||
|
if (!grouped[proName]) grouped[proName] = {};
|
||||||
|
if (!grouped[proName][leaseDate]) grouped[proName][leaseDate] = {};
|
||||||
|
if (!grouped[proName][leaseDate][typeFolder]) grouped[proName][leaseDate][typeFolder] = [];
|
||||||
|
|
||||||
|
grouped[proName][leaseDate][typeFolder].push(item);
|
||||||
|
});
|
||||||
|
|
||||||
|
// 拉平成后端需要的数组
|
||||||
|
const flatItems = [];
|
||||||
|
Object.keys(grouped).forEach(proName => {
|
||||||
|
Object.keys(grouped[proName]).forEach(leaseDate => {
|
||||||
|
Object.keys(grouped[proName][leaseDate]).forEach(typeFolder => {
|
||||||
|
grouped[proName][leaseDate][typeFolder].forEach(item => {
|
||||||
|
// 可以把 leaseDate 覆盖到 item.testTime,这样后端就可以直接使用
|
||||||
|
flatItems.push({ ...item, testTime: leaseDate });
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
// 计算总文件数
|
|
||||||
this.totalItems = this.calculateTotalFiles(items);
|
|
||||||
console.log('总文件数:', this.totalItems);
|
|
||||||
|
|
||||||
const taskId = crypto.randomUUID()
|
|
||||||
const payload = {
|
const payload = {
|
||||||
items: items,
|
items: flatItems,
|
||||||
taskId:taskId,
|
zipName: `报告下载_${(new Date()).toISOString().slice(0,10)}`,
|
||||||
zipName: `报告下载_${new Date().getTime()}`,
|
|
||||||
stream: true
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// 开始下载
|
this.downloadZip(
|
||||||
await this.streamDownload(payload);
|
'/material/bm_report/downloadBulk',
|
||||||
|
JSON.stringify(payload),
|
||||||
|
payload.zipName + '.zip'
|
||||||
|
);
|
||||||
|
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error('一键下载失败', err);
|
console.error('一键下载失败', err);
|
||||||
this.handleDownloadError(err);
|
this.$message.error('一键下载失败');
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
/** 初始化下载状态 */
|
|
||||||
initializeDownload() {
|
|
||||||
this.isExporting = true;
|
|
||||||
this.showProgress = true;
|
|
||||||
this.downloadComplete = false;
|
|
||||||
this.progressPercentage = 0;
|
|
||||||
this.currentItem = 0;
|
|
||||||
this.totalItems = 0;
|
|
||||||
this.currentFileName = '';
|
|
||||||
this.progressText = '准备下载...';
|
|
||||||
this.progressStatus = null;
|
|
||||||
this.downloadSpeed = 0;
|
|
||||||
this.retryCount = 0;
|
|
||||||
this.downloadedBytes = 0;
|
|
||||||
this.downloadStartTime = Date.now();
|
|
||||||
this.downloadController = new AbortController();
|
|
||||||
},
|
|
||||||
|
|
||||||
/** 计算总文件数 */
|
|
||||||
calculateTotalFiles(items) {
|
|
||||||
let total = 0;
|
|
||||||
items.forEach(item => {
|
|
||||||
// 出库检验报告
|
|
||||||
total += 1;
|
|
||||||
// 各个附件
|
|
||||||
if (item.qualifiedUrl) total += 1;
|
|
||||||
if (item.testReportUrl) total += 1;
|
|
||||||
if (item.thirdReportUrl) total += 1;
|
|
||||||
if (item.factoryReportUrl) total += 1;
|
|
||||||
if (item.otherReportUrl) total += 1;
|
|
||||||
});
|
|
||||||
return total;
|
|
||||||
},
|
|
||||||
|
|
||||||
/** 流式下载方法 - 使用原始的request方式 */
|
|
||||||
/** 流式下载方法 - 使用原始的request方式 */
|
|
||||||
async streamDownload(payload) {
|
|
||||||
try {
|
|
||||||
this.progressText = '正在连接服务器...';
|
|
||||||
|
|
||||||
const response = await request({
|
|
||||||
url: '/material/bm_report/downloadBulkStream',
|
|
||||||
method: 'POST',
|
|
||||||
data: payload,
|
|
||||||
responseType: 'blob',
|
|
||||||
timeout: 0,
|
|
||||||
headers: {
|
|
||||||
encryptRequest: false,
|
|
||||||
checkIntegrity: false,
|
|
||||||
encryptResponse: false
|
|
||||||
},
|
|
||||||
onDownloadProgress: (progressEvent) => {
|
|
||||||
this.handleDownloadProgress(progressEvent);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// 创建下载链接
|
|
||||||
const blob = new Blob([response]);
|
|
||||||
const url = window.URL.createObjectURL(blob);
|
|
||||||
const a = document.createElement('a');
|
|
||||||
a.style.display = 'none';
|
|
||||||
a.href = url;
|
|
||||||
a.download = `${payload.zipName}.zip`;
|
|
||||||
document.body.appendChild(a);
|
|
||||||
a.click();
|
|
||||||
|
|
||||||
window.URL.revokeObjectURL(url);
|
|
||||||
document.body.removeChild(a);
|
|
||||||
|
|
||||||
this.handleDownloadComplete();
|
|
||||||
|
|
||||||
} catch (error) {
|
|
||||||
if (error.message && error.message.includes('canceled')) {
|
|
||||||
this.progressText = '下载已取消';
|
|
||||||
this.progressPercentage = 0;
|
|
||||||
this.progressStatus = 'exception';
|
|
||||||
} else {
|
|
||||||
console.error('下载失败:', error);
|
|
||||||
this.handleDownloadError(error);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
|
||||||
|
|
||||||
|
|
||||||
/** 处理下载进度 */
|
|
||||||
handleDownloadProgress(progressEvent) {
|
|
||||||
if (progressEvent.total) {
|
|
||||||
// ZIP 文件流阶段
|
|
||||||
const percentage = Math.round((progressEvent.loaded * 100) / progressEvent.total);
|
|
||||||
this.progressPercentage = Math.min(percentage, 99);
|
|
||||||
|
|
||||||
const elapsed = Date.now() - this.downloadStartTime;
|
|
||||||
if (elapsed > 0) {
|
|
||||||
this.downloadSpeed = progressEvent.loaded * 1000 / elapsed;
|
|
||||||
this.progressText = `${this.formatBytes(progressEvent.loaded)} / ${this.formatBytes(progressEvent.total)}`;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// 后端处理文件阶段(无进度)
|
|
||||||
|
|
||||||
// ⭐⭐ 1. 下载中保证 currentItem 不超过总数
|
|
||||||
if (this.totalItems > 0) {
|
|
||||||
this.currentItem = Math.min(this.currentItem + 1, this.totalItems);
|
|
||||||
}
|
|
||||||
|
|
||||||
// ⭐ 2. 百分比始终不超过 100
|
|
||||||
this.progressPercentage = Math.min(
|
|
||||||
Math.round((this.currentItem / this.totalItems) * 100),
|
|
||||||
100
|
|
||||||
);
|
|
||||||
|
|
||||||
this.progressText = `处理中:${this.currentItem}/${this.totalItems}`;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/** 处理下载完成 */
|
|
||||||
handleDownloadComplete() {
|
|
||||||
// ⭐⭐ 2. 下载完成时补齐
|
|
||||||
this.currentItem = this.totalItems;
|
|
||||||
this.progressPercentage = 100;
|
|
||||||
|
|
||||||
this.progressText = '下载完成';
|
|
||||||
this.progressStatus = 'success';
|
|
||||||
this.downloadComplete = true;
|
|
||||||
this.isExporting = false;
|
|
||||||
this.downloadSpeed = 0;
|
|
||||||
|
|
||||||
this.$message({
|
|
||||||
message: '下载完成!',
|
|
||||||
type: 'success',
|
|
||||||
duration: 3000
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
|
|
||||||
/** 处理下载错误 */
|
|
||||||
handleDownloadError(error) {
|
|
||||||
console.error('下载失败:', error);
|
|
||||||
|
|
||||||
if (this.retryCount < this.maxRetries) {
|
|
||||||
this.retryCount++;
|
|
||||||
this.progressText = `下载失败,正在重试 (${this.retryCount}/${this.maxRetries})...`;
|
|
||||||
this.progressStatus = 'warning';
|
|
||||||
|
|
||||||
// 3秒后重试
|
|
||||||
setTimeout(() => {
|
|
||||||
this.handleBulkDownload();
|
|
||||||
}, 3000);
|
|
||||||
} else {
|
|
||||||
this.progressText = '下载失败,请重试';
|
|
||||||
this.progressStatus = 'exception';
|
|
||||||
this.isExporting = false;
|
|
||||||
this.$message.error('下载失败: ' + (error.message || '未知错误'));
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
/** 取消下载 */
|
|
||||||
cancelDownload() {
|
|
||||||
if (this.downloadController) {
|
|
||||||
this.downloadController.abort();
|
|
||||||
}
|
|
||||||
this.isExporting = false;
|
|
||||||
this.showProgress = false;
|
|
||||||
this.$message.info('下载已取消');
|
|
||||||
},
|
|
||||||
|
|
||||||
/** 清除进度显示 */
|
|
||||||
clearProgress() {
|
|
||||||
this.showProgress = false;
|
|
||||||
this.downloadComplete = false;
|
|
||||||
this.progressPercentage = 0;
|
|
||||||
this.currentItem = 0;
|
|
||||||
this.totalItems = 0;
|
|
||||||
this.downloadSpeed = 0;
|
|
||||||
this.progressStatus = null;
|
|
||||||
this.currentFileName = '';
|
|
||||||
},
|
|
||||||
|
|
||||||
/** 格式化字节大小 */
|
|
||||||
formatBytes(bytes, decimals = 2) {
|
|
||||||
if (bytes === 0) return '0 Bytes';
|
|
||||||
const k = 1024;
|
|
||||||
const dm = decimals < 0 ? 0 : decimals;
|
|
||||||
const sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB'];
|
|
||||||
const i = Math.floor(Math.log(bytes) / Math.log(k));
|
|
||||||
return parseFloat((bytes / Math.pow(k, i)).toFixed(dm)) + ' ' + sizes[i];
|
|
||||||
},
|
|
||||||
|
|
||||||
/** 单文件下载 */
|
|
||||||
async downloadSingleFile(payload, fileName) {
|
|
||||||
try {
|
|
||||||
this.isExporting = true;
|
|
||||||
|
|
||||||
const response = await request({
|
|
||||||
url: '/material/bm_report/downloadSingle',
|
|
||||||
method: 'post',
|
|
||||||
data: payload,
|
|
||||||
responseType: 'blob'
|
|
||||||
});
|
|
||||||
|
|
||||||
const blob = new Blob([response]);
|
|
||||||
const downloadUrl = window.URL.createObjectURL(blob);
|
|
||||||
const link = document.createElement('a');
|
|
||||||
link.href = downloadUrl;
|
|
||||||
link.download = fileName;
|
|
||||||
document.body.appendChild(link);
|
|
||||||
link.click();
|
|
||||||
document.body.removeChild(link);
|
|
||||||
window.URL.revokeObjectURL(downloadUrl);
|
|
||||||
|
|
||||||
this.$message.success('下载成功!');
|
|
||||||
|
|
||||||
} catch (error) {
|
|
||||||
console.error('下载失败:', error);
|
|
||||||
this.$message.error('下载失败');
|
|
||||||
} finally {
|
|
||||||
this.isExporting = false;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
.uploadImg {
|
.uploadImg {
|
||||||
padding-top: 20px;
|
padding-top: 20px;
|
||||||
|
|
@ -1245,175 +984,19 @@ export default {
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
.deviceCode {
|
.deviceCode {
|
||||||
margin-top: 10px;
|
margin-top: 10px;
|
||||||
padding-bottom: 20px;
|
padding-bottom: 20px;
|
||||||
font-size: 18px;
|
font-size: 18px;
|
||||||
}
|
}
|
||||||
|
|
||||||
::v-deep.el-table .fixed-width .el-button--mini {
|
::v-deep.el-table .fixed-width .el-button--mini {
|
||||||
width: 60px !important;
|
width: 60px !important;
|
||||||
margin-bottom: 10px;
|
margin-bottom: 10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
//隐藏图片上传框的css
|
//隐藏图片上传框的css
|
||||||
::v-deep.disabled {
|
::v-deep.disabled {
|
||||||
.el-upload--picture-card {
|
.el-upload--picture-card {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 按钮样式优化 */
|
|
||||||
.el-button {
|
|
||||||
transition: all 0.3s ease;
|
|
||||||
}
|
|
||||||
|
|
||||||
.el-button:disabled {
|
|
||||||
background-color: #f5f7fa;
|
|
||||||
border-color: #e4e7ed;
|
|
||||||
color: #c0c4cc;
|
|
||||||
cursor: not-allowed;
|
|
||||||
}
|
|
||||||
|
|
||||||
.el-button--mini {
|
|
||||||
padding: 7px 15px;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 下载按钮激活状态 */
|
|
||||||
.el-button:not(:disabled):hover {
|
|
||||||
transform: translateY(-1px);
|
|
||||||
box-shadow: 0 2px 8px rgba(64, 158, 255, 0.2);
|
|
||||||
}
|
|
||||||
|
|
||||||
.el-button--primary:not(:disabled) {
|
|
||||||
background-color: #409eff;
|
|
||||||
border-color: #409eff;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 下载按钮加载状态 */
|
|
||||||
.el-button.is-loading {
|
|
||||||
position: relative;
|
|
||||||
pointer-events: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.el-button.is-loading:before {
|
|
||||||
content: '';
|
|
||||||
position: absolute;
|
|
||||||
left: -1px;
|
|
||||||
top: -1px;
|
|
||||||
right: -1px;
|
|
||||||
bottom: -1px;
|
|
||||||
border-radius: inherit;
|
|
||||||
background-color: rgba(255, 255, 255, 0.35);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 进度条容器样式 */
|
|
||||||
.progress-container {
|
|
||||||
margin: 20px 0;
|
|
||||||
padding: 20px;
|
|
||||||
border: 1px solid #e0e0e0;
|
|
||||||
border-radius: 8px;
|
|
||||||
background-color: #f9f9f9;
|
|
||||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
|
|
||||||
animation: fadeIn 0.3s ease;
|
|
||||||
}
|
|
||||||
|
|
||||||
@keyframes fadeIn {
|
|
||||||
from {
|
|
||||||
opacity: 0;
|
|
||||||
transform: translateY(-10px);
|
|
||||||
}
|
|
||||||
to {
|
|
||||||
opacity: 1;
|
|
||||||
transform: translateY(0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.progress-info {
|
|
||||||
margin-top: 15px;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
align-items: center;
|
|
||||||
gap: 5px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.progress-text {
|
|
||||||
font-size: 14px;
|
|
||||||
color: #333;
|
|
||||||
font-weight: 500;
|
|
||||||
}
|
|
||||||
|
|
||||||
.progress-detail {
|
|
||||||
font-size: 12px;
|
|
||||||
color: #666;
|
|
||||||
}
|
|
||||||
|
|
||||||
.progress-speed {
|
|
||||||
font-size: 12px;
|
|
||||||
color: #409eff;
|
|
||||||
font-weight: 500;
|
|
||||||
}
|
|
||||||
|
|
||||||
.progress-actions {
|
|
||||||
margin-top: 15px;
|
|
||||||
display: flex;
|
|
||||||
justify-content: center;
|
|
||||||
gap: 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Element UI Progress 自定义样式 */
|
|
||||||
::v-deep .el-progress-bar__inner {
|
|
||||||
background: linear-gradient(90deg, #409eff 0%, #67c23a 100%);
|
|
||||||
transition: width 0.3s ease;
|
|
||||||
}
|
|
||||||
|
|
||||||
::v-deep .el-progress-bar__innerText {
|
|
||||||
color: #fff;
|
|
||||||
font-weight: bold;
|
|
||||||
text-shadow: 0 1px 1px rgba(0, 0, 0, 0.2);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 响应式调整 */
|
|
||||||
@media (max-width: 768px) {
|
|
||||||
.progress-container {
|
|
||||||
padding: 15px;
|
|
||||||
margin: 15px 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.progress-text {
|
|
||||||
font-size: 13px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.progress-detail,
|
|
||||||
.progress-speed {
|
|
||||||
font-size: 11px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.el-button--mini {
|
|
||||||
min-width: 80px;
|
|
||||||
padding: 5px 12px;
|
|
||||||
font-size: 12px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 表格复选框高亮 */
|
|
||||||
::v-deep .el-table__row.selected-row {
|
|
||||||
background-color: #f0f9ff;
|
|
||||||
}
|
|
||||||
|
|
||||||
::v-deep .el-table__row:hover {
|
|
||||||
background-color: #f5f7fa;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 复选框样式修复 */
|
|
||||||
::v-deep .el-table .el-checkbox__input.is-checked .el-checkbox__inner {
|
|
||||||
background-color: #409eff;
|
|
||||||
border-color: #409eff;
|
|
||||||
}
|
|
||||||
|
|
||||||
::v-deep .el-table .el-checkbox__input.is-indeterminate .el-checkbox__inner {
|
|
||||||
background-color: #409eff;
|
|
||||||
border-color: #409eff;
|
|
||||||
}
|
|
||||||
</style>
|
</style>
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue