feat(report): 事件历史记录添加文件预览功能

- 新增文件预览模块,支持图片和 PDF 文件的查看
- 实现放大、缩小、旋转等功能
- 优化对话框布局,支持全屏预览
- 修改相关组件以适应新功能
master
Tuzki 6 months ago
parent 2ebda16068
commit 6d45c36fdb
  1. 339
      src/views/report/eventHistory/index.vue
  2. 5
      src/views/report/eventList/EquipmentRegistrationForm.vue
  3. 39
      src/views/report/eventList/printDemo.vue

@ -8,13 +8,12 @@
<el-form class="m-search-form" :class="searchState ? 'col' : ''" :model="queryParams" ref="queryForm"
:inline="true" v-show="showSearch" label-width="">
<el-form-item label="单位名称/姓名" prop="userUnit">
<el-input v-model="queryParams.userUnit" placeholder="请输入单位名称/姓名"
@keyup.enter.native="handleQuery" />
<el-input v-model="queryParams.userUnit" placeholder="请输入单位名称/姓名" @keyup.enter.native="handleQuery" />
</el-form-item>
<el-form-item label="创建时间" prop="createTime" label-width="100px">
<el-date-picker v-model="queryParams.createTime" :clearable="false" style="width: 240px" value-format="yyyy-MM-dd HH:mm:ss"
type="daterange" range-separator="-" start-placeholder="开始日期" end-placeholder="结束日期"
:default-time="['00:00:00', '23:59:59']" />
<el-date-picker v-model="queryParams.createTime" :clearable="false" style="width: 240px"
value-format="yyyy-MM-dd HH:mm:ss" type="daterange" range-separator="-" start-placeholder="开始日期"
end-placeholder="结束日期" :default-time="['00:00:00', '23:59:59']" />
</el-form-item>
</el-form>
<div class="m-search-button">
@ -33,7 +32,7 @@
<template v-else>
<div class="event-history-item" v-for="item in list">
<div class="event-history-item-left">
<div class="enent-name">{{ item.userUnit+'-'+item.licensePlate || '--' }}</div>
<div class="enent-name">{{ item.userUnit + '-' + item.licensePlate || '--' }}</div>
<div class="event-history-item-time">创建时间:{{ parseTime(item.createTime) }}</div>
</div>
<div class="event-history-item-right">
@ -47,9 +46,67 @@
<pagination class="m-pagination" v-show="total > 0" :total="total" :page.sync="queryParams.pageNo"
:limit.sync="queryParams.pageSize" @pagination="getList" />
</div>
<el-dialog class="m-dialog" title="记录详情" :visible.sync="open" width="1000px" v-dialogDrag append-to-body>
<div v-if="Object.keys(formData).length === 0">加载中...</div>
<EquipmentRegistrationForm :disabled="formDisabled" v-else :initialData="formData" />
<el-dialog class="m-dialog" title="记录详情" :visible.sync="open" width="1000px" v-dialogDrag append-to-body fullscreen>
<div class="view-box">
<div class="file-view-box" v-if="fileList && fileList.length > 0">
<div class="list-scroll-nomore">
<div class="list-scroll-box">
<div v-for="item in fileList" class="file-item" @click="bigView(item)">
<img style="width: 100%;" v-if="item.type === 'image'" :src="item.url" class="preview-image">
<pdf v-if="item.type === 'pdf'" :src="item.url"></pdf>
</div>
</div>
</div>
<div class="file-view-content">
<div class="btn-box">
<div class="pagination">
<el-button style="font-size: 22px;padding: 5px 15px;" @click="zoomIn">+</el-button>
<el-button style="font-size: 22px;padding: 5px 15px;" @click="zoomOut">-</el-button>
</div>
<div class="pagination" v-if="showType === 'image'">
<el-button @click="rotateClockwise" style="font-size: 22px;padding: 5px 15px;">&#x27F3;</el-button>
<el-button @click="rotateCounterClockwise"
style="font-size: 22px;padding: 5px 15px;">&#x27F2;</el-button>
</div>
<div class="pagination" v-if="showType === 'pdf' && pageCount <= 1">
<el-button @click="rotates += 90" style="font-size: 22px;padding: 5px 15px;">&#x27F3;</el-button>
<el-button @click="rotates -= 90" style="font-size: 22px;padding: 5px 15px;">&#x27F2;</el-button>
</div>
<div class="pagination" v-if="showType === 'pdf' && pageCount > 1">
<el-button @click="rotates += 90" style="font-size: 22px;padding: 5px 15px;">&#x27F3;</el-button>
<el-button @click="rotates -= 90" style="font-size: 22px;padding: 5px 15px;">&#x27F2;</el-button>
<el-button @click="currentPage = currentPage - 1" :disabled="currentPage <= 1"
style="font-size: 14px;padding: 10px 15px;">上一页</el-button>
<span style="font-size: 14px;margin: 0 10px;"> {{ currentPage }} / {{ pageCount }} </span>
<el-button @click="currentPage = currentPage + 1" :disabled="currentPage >= pageCount"
style="font-size: 14px;padding: 10px 15px;">下一页</el-button>
</div>
</div>
<div class="nomore-view">
<div class="pdf-img-box drag-box" :style="showType === 'image' ? boxStyle : boxStyle1"
@mousedown="startDrag">
<img v-if="showType === 'image'" :src="showUrl" class="preview-image" draggable="false"
@mousedown.prevent @dragstart.prevent>
<pdf style="width: 100%;" v-if="showType === 'pdf'" :src="showUrl" :rotate="rotates" :page="currentPage"
@num-pages="pageCount = $event" @progress="isLoading = true" @loaded="isLoading = false"></pdf>
</div>
<div class="mask" v-if="isLoading">加载中...</div>
</div>
</div>
</div>
<div class="export-bx">
<div class="word-view-box">
<div v-if="Object.keys(formData).length === 0">加载中...</div>
<EquipmentRegistrationForm :disabled="formDisabled" v-else :initialData="formData" />
</div>
</div>
</div>
</el-dialog>
</div>
@ -60,18 +117,45 @@
import VMixins from '@/utils/v-mixins'
import VDefault from '@/views/components/v-default/index.vue'
import EquipmentRegistrationForm from '../eventList/EquipmentRegistrationForm.vue'
import pdf from 'vue-pdf';
export default {
name: 'TicketSort',
mixins: [VMixins],
components: {
VDefault,
EquipmentRegistrationForm
EquipmentRegistrationForm,
pdf
},
data() {
return {
open: false,
formDisabled: false,
fileList: [],
currentPage: 1, //
pageCount: 0, //
isLoading: false, //
rotate: 0,
rotates: 0,
showUrl: '',
showType: '',
scale: 1, //
position: { //
x: 0,
y: 0
},
dragging: false, //
startX: 0, // X
startY: 0, // Y
initialX: 0, // X
initialY: 0, // Y
centerOffset: { x: 0, y: 0 }, //
progress: {
visible: false,
current: 0,
total: 0,
text: ''
},
//
loading: true,
//
@ -82,7 +166,7 @@
total: 1,
//
list: [
],
formData: {},
//
@ -103,6 +187,31 @@
created() {
this.getList()
},
computed: {
//
boxStyle() {
return {
transform: `
translate(${this.position.x}px, ${this.position.y}px)
translate(${this.centerOffset.x}px, ${this.centerOffset.y}px)
rotate(${this.rotate}deg)
scale(${this.scale})
translateZ(0)
`,
transformOrigin: 'center center', //
position: 'absolute',
cursor: this.dragging ? 'grabbing' : 'grab',
};
},
boxStyle1() {
return {
transform: `translate(${this.position.x}px, ${this.position.y}px) scale(${this.scale})`,
transformOrigin: '0 0', //
position: 'absolute',
cursor: this.dragging ? 'grabbing' : 'grab'
};
}
},
methods: {
/** 查询列表 */
async getList() {
@ -121,7 +230,11 @@
const res = await TicketSortApi.getEquipmentt(val.id)
this.formData = res.data
this.formData.infoList = JSON.parse(this.formData.infoList)
this.formData.commissionDate = this.formData.commissionDate.join('-')
// this.formData.commissionDate = this.formData.commissionDate.join('-')
this.fileList = this.formData.fileUrl ? JSON.parse(this.formData.fileUrl) : []
if (this.fileList.length > 0) {
this.bigView(this.fileList[0])
}
console.log(res)
},
/** 搜索按钮操作 */
@ -163,12 +276,208 @@
} finally {
this.exportLoading = false
}
},
bigView(val) {
//
this.position = {
x: 0,
y: 0
}
this.centerOffset = {
x: 0,
y: 0
}
this.rotate = 0
this.scale = 1
this.dragging = false
this.currentPage = 1;
//
this.showUrl = ''
this.showUrl = val.url;
this.showType = val.type;
this.$nextTick(() => {
const box = this.$el.querySelector('.drag-box');
if (box) {
this.centerOffset = {
x: 0,
y: 0
};
}
});
},
//
zoomIn() {
this.scale = Math.min(this.scale * 1.2, 4); //
},
//
zoomOut() {
this.scale = Math.max(this.scale * 0.8, 0.5); //
},
//
startDrag(e) {
this.dragging = true;
this.startX = e.clientX;
this.startY = e.clientY;
this.initialX = this.position.x;
this.initialY = this.position.y;
document.addEventListener('mousemove', this.onDrag);
document.addEventListener('mouseup', this.stopDrag);
},
//
onDrag(e) {
if (!this.dragging) return;
const dx = e.clientX - this.startX;
const dy = e.clientY - this.startY;
this.position.x = this.initialX + dx;
this.position.y = this.initialY + dy;
},
//
stopDrag() {
this.dragging = false;
document.removeEventListener('mousemove', this.onDrag);
document.removeEventListener('mouseup', this.stopDrag);
}
}
}
</script>
<style scoped lang="scss">
.event-history-item:not(:last-child){
margin-bottom: 10px;
}
.event-history-item:not(:last-child) {
margin-bottom: 10px;
}
.view-box {
display: flex;
align-items: flex-start;
width: 100%;
justify-content: space-between;
overflow-y: scroll;
}
.file-view-box {
flex: 1;
display: flex;
align-items: flex-start;
justify-content: space-between;
overflow-y: scroll;
height: calc(100vh - 125px);
overflow-y: scroll;
background-color: #fff;
padding: 15px;
scrollbar-width: none;
margin-right: 10px;
.list-scroll-nomore {
margin-top: 55px;
height: 77vh;
overflow-y: scroll;
scrollbar-width: none;
width: 10%;
}
.list-scroll-box {
height: fit-content;
background-color: #eef1f9;
padding: 5px;
.file-item {
width: 100%;
background: #fff;
padding: 5px 0;
}
.file-item:not(:last-child) {
margin-bottom: 15px;
cursor: pointer;
}
}
.file-view-content {
flex: 1;
margin-left: 10px;
position: relative;
.btn-box {
display: flex;
align-items: center;
justify-content: center;
.pagination {
margin: 10px;
text-align: center;
display: flex;
align-items: center;
}
}
.nomore-view {
width: 100%;
height: calc(100vh - 200px);
background-color: #eef1f9;
overflow: hidden;
position: relative;
.pdf-img-box {
overflow: scroll;
scrollbar-width: none;
/* Firefox */
-ms-overflow-style: none;
/* Internet Explorer 10+ */
}
.drag-box {
width: 100%;
height: fit-content;
background: #f0f0f0;
display: flex;
align-items: center;
justify-content: center;
user-select: none;
img {
width: 100%;
user-select: none;
-webkit-user-drag: none;
/* Safari/Chrome */
pointer-events: none;
/* 可选:彻底禁止图片触发事件 */
}
/* 防止文字被选中 */
}
.mask {
background-color: rgba(0, 0, 0, 0.5);
height: 100%;
width: 100%;
color: #fff;
text-align: center;
line-height: 100vh;
z-index: 9999;
position: relative;
}
}
}
}
.export-bx {
background-color: #fff;
position: relative;
height: calc(100vh - 126px);
overflow-y: scroll;
scrollbar-width: none;
}
.word-view-box {
width: fit-content;
min-width: 50%;
height: calc(100vh - 200px);
overflow-y: scroll;
scrollbar-width: thin !important;
scrollbar-color: #7dbdfc #f5f5f5;
}
</style>

@ -265,7 +265,8 @@
"telephone": "",//
"safetyManager": "",//
"mobilePhone": "",//
"infoList": []
"infoList": [],
"fileUrl":""
},
rules: {
equipmentType: [{ required: true, message: '设备品种不能为空', trigger: 'blur' }],
@ -281,7 +282,7 @@
// { validator: validateNumber('L') }
],
contractor: [{ required: true, message: '施工单位不能为空', trigger: 'blur' }],
supervisionAgency: [{ required: true, message: '监督检验机构不能为空', trigger: 'blur' }],
// supervisionAgency: [{ required: true, message: '', trigger: 'blur' }],
userUnit: [{ required: true, message: '使用单位不能为空', trigger: 'blur' }],
userAddress: [{ required: true, message: '使用地址不能为空', trigger: 'blur' }],
unifiedSocialCode: [{ required: true, message: '统一信用代码不能为空', trigger: 'blur' }],

@ -25,8 +25,8 @@
<el-button @click="rotates -= 90" style="font-size: 22px;padding: 5px 15px;">&#x27F2;</el-button>
</div>
<div class="pagination" v-if="showType === 'pdf' && pageCount > 1">
<el-button @click="rotate += 90" style="font-size: 22px;padding: 5px 15px;">&#x27F3;</el-button>
<el-button @click="rotate -= 90" style="font-size: 22px;padding: 5px 15px;">&#x27F2;</el-button>
<el-button @click="rotates += 90" style="font-size: 22px;padding: 5px 15px;">&#x27F3;</el-button>
<el-button @click="rotates -= 90" style="font-size: 22px;padding: 5px 15px;">&#x27F2;</el-button>
<el-button @click="currentPage = currentPage - 1" :disabled="currentPage <= 1"
style="font-size: 14px;padding: 10px 15px;">上一页</el-button>
<span style="font-size: 14px;margin: 0 10px;"> {{ currentPage }} / {{ pageCount }} </span>
@ -338,9 +338,14 @@
const maxAgencies = agencies.slice(0, 3);
// 3.
formData.supervisionAgency = maxAgencies.length > 0
? maxAgencies.join('/ ')
: '';
if (formData.fillingMedium == "LNG" || formData.fillingMedium == "") {
formData.supervisionAgency = maxAgencies.length > 0
? maxAgencies.join('/ ')
: '';
} else {
formData.supervisionAgency = ""
}
} catch (e) {
console.error('监督检验机构处理失败:', e);
formData.supervisionAgency = '';
@ -419,7 +424,7 @@
internalId: ''
};
});
formData.cylinderQuantity = tempInfoList.length;
// 5. 使ID3
while (tempInfoList.length < 3 && remainingIds.length > 0) {
tempInfoList.push({
@ -546,13 +551,24 @@
]
}
}
// extractedData
const currentType = item.type
const isIdCard = currentType === 'idCard'
const isVehicle = currentType === 'vehicleLicense'
// extractedData
debugger
return {
...acc,
...item.extractedData //
...acc, //
...item.extractedData, //
// userAddress
userAddress: isIdCard
? (item.extractedData.userAddress || acc.userAddress) // idCard
: isVehicle
? (acc.userAddress || item.extractedData.userAddress) // vehicle
: acc.userAddress //
}
}
return acc;
return acc
}, {
registrationCategory: this.formData.registrationCategory,
equipmentType: "特种气瓶",//
@ -579,7 +595,8 @@
mobilePhone: "",//
postalCode: "050200",//
certificateList: [], //
infoList: []
infoList: [],
fileUrl: JSON.stringify(this.fileList)
});
console.log('合并结果', mergedData);
// const mergedData = {
@ -720,7 +737,7 @@
// ]
// }
this.calculateCylinderQuantity(mergedData);//
// this.calculateCylinderQuantity(mergedData);//
this.processFillingMedium(mergedData);//
this.processNominalPressure(mergedData);//
this.processCylinderVolume(mergedData);//

Loading…
Cancel
Save