Files
app-govern/pages/index/comp/transient.vue
2026-07-02 09:55:30 +08:00

495 lines
17 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<template>
<view class="dateReport">
<view class="pd20">
<uni-segmented-control :current="curSub" class="subsection" active-color="#376cf3" :values="subsectionList"
@clickItem="sectionChange" />
</view>
<!-- 申请报告 -->
<view v-show="curSub == 0">
<!-- apply -->
<Apply ref="applyRef" :navHeight="navHeight" />
</view>
<!-- 申请记录 -->
<view v-if="curSub == 1" style="position: relative">
<view class="filterCriteria">
<!-- 筛选条件 -->
<Cn-filterCriteria @select="select" :showQianTree="false"> </Cn-filterCriteria>
</view>
<scroll-view scroll-y="true" @scroll="onScroll" :scroll-top="scrollTop" scroll-with-animation
@refresherrefresh="refresherrefresh" @scrolltolower="scrolltolower" :refresher-triggered="triggered"
refresher-enabled="true" class="record event-list mt20"
:style="{ height: 'calc(100vh - ' + (navHeight + height) + 'px)', overflow: 'auto' }">
<uni-card class="event-item" :class="item.type" v-for="(item, index) in store.data" :key="index">
<!-- 头部图标 + 信息 + 操作 -->
<view class="event-header">
<view class="event-icon">
<!-- :style="{ backgroundColor: item.isComplete == 1 ? '#10b98120' : '#FF000020' }" -->
<!-- 动态图标根据类型切换 -->
<!-- <uni-icons
custom-prefix="iconfont"
type="icon-baogaoguanli"
size="40"
:color="item.isComplete == 1 ? '#10b981' : '#FF0000'"
></uni-icons> -->
<Cn-icon-transient :name="`报告`" />
</view>
<view class="event-info">
<view class="event-title">
<text class="event-id">{{ item.lineName }}</text>
</view>
<view class="event-desc">
<text>工程{{ item.engineeringName }}</text>
<text>项目{{ item.projectName }}</text>
<text>设备{{ item.deviceName }}</text>
<!-- <text>申请时间{{ item.time }}</text>
<text>事件数{{ item.eventNums }}</text> -->
</view>
</view>
<view class="event-action">
<view class="iconText boxClick" v-if="item.isComplete == 1" @click="download(item)">
<uni-icons type="arrow-down" color="#fff" size="16"></uni-icons>
</view>
<view class="nav-menu nav-menu-btn boxClick"
v-else-if="userInfo.authorities === 'operation_manager'" @click="generate(item)">生成报告
</view>
</view>
</view>
<!-- 详情区域 -->
<view class="event-detail">
<!-- <view class="device-body"> -->
<view class="device-body-item">
<text>申请人</text>
<text>{{ item.applyUser }}</text>
</view>
<view class="device-body-item">
<text>报告状态</text>
<text :style="{ color: item.isComplete == 1 ? '#10b981' : '#FF0000' }">{{
item.isComplete == 1 ? '已完成' : '未完成'
}}</text>
</view>
<view class="device-body-item">
<text>申请时间</text>
<text>{{ item.time }}</text>
</view>
<view class="device-body-item">
<text>暂降事件</text>
<text>{{ item.eventNums }}</text>
</view>
<!-- <view class="device-body-item">
<text>申请时间</text>
<text>{{ item.time }}</text>
</view> -->
</view>
<!-- </view> -->
</uni-card>
<uni-load-more v-if="store.status == 'loading' || (store.data && store.data.length > 0)"
:status="store.status"></uni-load-more>
<Cn-empty v-else style="top: 30%"></Cn-empty>
</scroll-view>
<view class="back-top boxClick" v-show="showBackTop" @click="backToTop">
<uni-icons type="arrow-up" size="22" color="#fff"></uni-icons>
</view>
</view>
</view>
</template>
<script>
import Apply from './apply.vue'
import list from '@/common/js/list'
import { createEventReport, downloadEventReport } from '@/common/api/report.js'
export default {
components: { Apply },
props: {
indexList: {
type: Array,
default: () => [],
},
total: {
type: Number,
default: 0,
},
// status: {
// type: String,
// default: 'more',
// },
navHeight: {
type: Number,
default: 0,
},
},
mixins: [list],
data() {
return {
value: ['0'],
content: 123,
curSub: 0,
subsectionList: ['申请报告', '申请记录'],
form: {
type: 0,
lindId: '',
},
triggered: true,
userInfo: {},
height: 0,
selectValue: {},
scrollTop: 0,
oldScrollTop: 0,
showBackTop: false,
}
},
created() { },
mounted() {
this.userInfo = uni.getStorageSync(this.$cacheKey.userInfo)
},
computed: {},
methods: {
setHeight() {
uni.createSelectorQuery()
.select('.filterCriteria')
.boundingClientRect((rect) => {
//
// #ifdef H5
this.height = rect?.height + 180 || 0
// #endif
// #ifdef APP-PLUS
this.height = rect?.height + 110 || 0
// #endif
})
.exec()
},
init() {
this.store = this.DataSource('/cs-report-boot/csAppReport/getApplicationReport')
this.store.params.startTime = this.$util.getMonthFirstAndLastDay(this.selectValue.date).firstDay
this.store.params.endTime = this.$util.getMonthFirstAndLastDay(this.selectValue.date).lastDay
this.store.loadedCallback = () => {
this.setHeight()
}
this.store.reload()
},
async select(val) {
this.selectValue = val
await this.init()
},
sectionChange(index) {
this.curSub = index.currentIndex
this.showBackTop = false
this.scrollTop = 0
},
// 生成报告
generate(item) {
uni.showLoading({
title: '生成中,请稍等...',
mask: true,
})
createEventReport({
id: item.eventId,
}).then((res) => {
uni.showToast({
icon: 'success',
mask: true,
title: '生成报告成功!',
duration: 1000,
})
this.store.reload()
}).catch((err) => {
uni.showToast({
icon: 'none',
mask: true,
title: err.message,
duration: 1000,
})
})
},
// 下载报告
download(item) {
uni.showLoading({
title: '下载中,请稍等...',
mask: true,
})
downloadEventReport({
id: item.eventId,
})
.then((res) => {
const pathMatch = res.data.match(/excelreport([^?]*)/)
if (!pathMatch) {
uni.hideLoading()
uni.showToast({
icon: 'none',
mask: true,
title: '文件地址无效',
duration: 1000,
})
return
}
const fileMeta = this.getReportFileMeta(pathMatch[1])
const match = this.$config.static + pathMatch[1]
uni.downloadFile({
url: match,
success: (downloadRes) => {
if (downloadRes.statusCode === 200) {
this.openReportFile(downloadRes.tempFilePath, fileMeta)
} else {
uni.hideLoading()
uni.showToast({
icon: 'none',
mask: true,
title: '下载失败!',
duration: 1000,
})
}
},
fail: () => {
uni.hideLoading()
uni.showToast({
icon: 'none',
mask: true,
title: '下载失败!',
duration: 1000,
})
},
})
})
.catch((err) => {
uni.hideLoading()
uni.showToast({
icon: 'none',
mask: true,
title: err.message,
duration: 1000,
})
})
},
getReportFileMeta(filePath) {
const lower = (filePath || '').toLowerCase()
if (lower.endsWith('.doc')) {
return { fileType: 'doc', ext: 'doc' }
}
return { fileType: 'docx', ext: 'docx' }
},
openReportFile(tempFilePath, fileMeta) {
const previewFile = (filePath) => {
uni.hideLoading()
uni.openDocument({
filePath,
fileType: fileMeta.fileType,
showMenu: true,
success: () => {
console.log('成功打开文件')
},
fail: (err) => {
console.log('打开文件失败', err)
uni.showToast({
icon: 'none',
title: '预览失败',
})
},
})
}
// #ifdef APP-PLUS
// iOS 需保留 doc/docx 后缀saveFile 随机路径会导致 openDocument 失败
if (plus.os.name === 'iOS') {
const fileName = `event_report_${Date.now()}.${fileMeta.ext}`
plus.io.resolveLocalFileSystemURL(
tempFilePath,
(entry) => {
plus.io.resolveLocalFileSystemURL(
'_doc/',
(docEntry) => {
entry.copyTo(
docEntry,
fileName,
(newEntry) => previewFile(newEntry.fullPath),
() => previewFile(tempFilePath)
)
},
() => previewFile(tempFilePath)
)
},
() => previewFile(tempFilePath)
)
return
}
// #endif
uni.saveFile({
tempFilePath,
success: (data) => {
uni.showToast({
icon: 'success',
mask: true,
title: '下载成功!',
duration: 1000,
})
setTimeout(() => previewFile(data.savedFilePath), 500)
},
fail: () => {
uni.hideLoading()
uni.showToast({
icon: 'none',
title: '保存失败!',
})
},
})
},
// 刷新
reload() {
switch (this.curSub) {
case 0:
this.$refs.applyRef.store.reload()
break
case 1:
this.store && this.store.reload()
break
}
},
// 下拉
refresherrefresh() {
this.triggered = true
uni.startPullDownRefresh()
setTimeout(() => {
this.triggered = false
}, 500)
},
// 上拉
scrolltolower() {
if (this.store.status != 'noMore') {
this.store.next && this.store.next()
}
},
onScroll(e) {
this.oldScrollTop = e.detail.scrollTop
this.showBackTop = e.detail.scrollTop > 200
},
backToTop() {
this.scrollTop = this.oldScrollTop
this.$nextTick(() => {
this.scrollTop = 0
})
this.showBackTop = false
},
},
watch: {},
}
</script>
<style lang="scss" scoped>
@import '@/pages/message1/index.scss';
.event-detail {
display: grid;
grid-template-columns: 1.6fr 1fr;
}
// /deep/ .record {
// .uni-card__content {
// padding: 55rpx 20rpx 20rpx !important;
// }
// }
.filterCriteria {
.nav {
background-color: #fff;
}
.choose1 {
background-color: #fff;
padding: 0 20rpx;
display: flex;
justify-content: space-between;
/deep/ .uni-checkbox-input {
width: 30rpx;
height: 30rpx;
}
font-size: 26rpx;
}
}
// .device-body {
// padding: 10rpx 20rpx 20rpx;
.device-body-item {
display: flex;
// justify-content: space-between;
font-size: 26rpx;
color: #666666;
line-height: 1.5;
}
// }
.iconText {
display: flex;
width: 45rpx;
height: 45rpx;
border-radius: 50%;
background-color: $uni-theme-color;
justify-content: center;
align-items: center;
}
.nav-menu {
height: 40rpx;
padding: 6rpx 20rpx;
font-size: 24rpx;
border-radius: 8rpx;
line-height: 40rpx;
background: #ebeaec;
color: #666;
&-active {
background: #dfe5f7;
color: $uni-theme-color;
}
&-btn {
background: $uni-theme-color;
color: #fff;
}
}
.event-list {
/* 图标区域(按类型区分背景色) */
.event-icon {
background-color: #376cf320;
}
}
.segmented-control {
flex: 1;
margin-right: 24rpx;
height: 60rpx;
}
/deep/ .uni-scroll-view-refresher {
display: none;
}
.back-top {
position: fixed;
right: 30rpx;
bottom: 60rpx;
width: 80rpx;
height: 80rpx;
border-radius: 50%;
background: #376cf3;
display: flex;
align-items: center;
justify-content: center;
box-shadow: 0 4rpx 16rpx rgba(55, 108, 243, 0.35);
z-index: 1;
}
/deep/ .segmented-control__text {
line-height: 60rpx !important;
}
</style>