mobile-workflow/pages/instance/processProgress.vue
2024-05-25 20:47:31 +08:00

353 lines
8.0 KiB
Vue

<template>
<view class="w-process">
<view
:class="{'w-process-node': true, 'w-cmt-node': (node.comment && node.comment.length > 0) || node.signature}"
v-for="(node, index) in (progress || [])" :key="index">
<view class="w-process-item-line"></view>
<view class="w-process-item">
<view class="w-process-item-avatar">
<avatar :size="42" :status="getStatus(node)" :name="node.user.name" :src="getRes(node.user.avatar)"
:showName="false"></avatar>
</view>
<view class="w-p-text-info">
<view>{{node.name}} <text
v-show="(node.comment && node.comment.length > 0) || node.signature">(添加了评论)</text></view>
<view>{{node.user.name}}</view>
</view>
<text class="w-p-time">
{{ node.isFuture ? '等待中' : getFinishTime(node.finishTime) }}
</text>
</view>
<view class="w-process-node-content">
<view class="w-comments" v-show="(node.comment && node.comment.length > 0) || node.signature">
<view v-if="(node.signature || '') !== ''"
style="display: flex; align-items: flex-start; margin-bottom: 5px;">
<text>签字:</text>
<image mode="aspectFit" :src="node.signature" style="width: 150rpx; height: 100rpx;"></image>
</view>
<view v-for="cmt in node.comment" :key="cmt.id" class="w-comment">
<view>{{cmt.text}}</view>
<template v-if="cmt.attachments.length > 0">
<view>
<image mode="aspectFit" :src="img" v-for="(img, i) in filterImages(cmt.attachments)"
:key="img" @click="previewImg(img, cmt.attachments)"></image>
</view>
<view>
<view class="ellipsis" v-for="(file, i) in filterFiles(cmt.attachments)" :key="file.id">
<view style="color: #4C87F3;" @click="downloadFile(file.url)">{{file.name}}</view>
</view>
<!-- <text v-for="(file, i) in filterFiles(cmt.attachments)" :key="file.id">
<text v-if="typeIsImg(file.name)" style="color: #4C87F3;"
@click="previewImgOne(file)">{{file.name}}</text>
<uni-link v-else :showUnderLine="false" color="#4C87F3" :download="file.name"
:href="file.url" :text="file.name"></uni-link>
</text> -->
</view>
</template>
</view>
</view>
</view>
</view>
<view class="w-process-end w-process-item">
<view :style="{width: 50 / 16 + 'rem', height: 50 / 16 * 0.8 + 'rem'}">
<uni-icons :size="30" type="more-filled" color="#8b8b8b" class="w-pr-running"
v-if="result === 'RUNNING'"></uni-icons>
<uni-icons :size="30" type="checkbox-filled" color="#5FB685" class="w-pr-status"
v-else-if="result === 'PASS'"></uni-icons>
<uni-icons :size="30" type="clear" color="#CE5266" class="w-pr-status"
v-else-if="result === 'REFUSE'"></uni-icons>
<uni-icons :size="30" type="close" color="#8b8b8b" class="w-pr-status"
v-else-if="result === 'CANCEL'"></uni-icons>
</view>
<view>{{status}}</view>
</view>
<view class="fixed" :class="{show:showIframe}" v-if="show" @click="show=false">
<view class="iframeMain" @click.stop="show=true">
<video v-if="getType(iframeUrl) == 'video'" class="iframe" :src="iframeUrl"></video>
<image v-else-if="getType(iframeUrl) == 'image'" class="iframe" :src="iframeUrl" mode="aspectFill"></image>
<iframe v-else class="iframe" ref="iframe" :src="iframeUrl" frameborder="0"></iframe>
</view>
</view>
</view>
</template>
<script setup>
import Avatar from '@/components/Avatar.vue'
import {
nextTick,
ref
} from 'vue';
import {
getRes,
getFileSize,
isVideoLink,
isImageLink
} from '@/utils/tool.js'
import {
Log
} from '@icon-park/vue-next';
const props = defineProps(['progress', 'status', 'result'])
const imgViews = ref([])
const showIframe = ref(false)
const show = ref(false)
const iframeUrl = ref("")
const iframe = ref()
function getFinishTime(time) {
if (time) {
return time.substring(5, 16)
}
return '处理中'
}
function filterImages(attachments) {
return (attachments || []).filter(f => f.isImage).map(f => {
console.log(f.url);
return getRes(f.url)
})
}
function filterFiles(attachments) {
return (attachments || []).filter(f => !f.isImage).map(f => {
return {
...f,
url: getRes(f.url)
}
})
}
function previewImg(img, attachments) {
console.log(img, attachments);
const imgs = filterImages(attachments)
console.log(imgs);
uni.previewImage({
current: imgs.indexOf(img),
urls: imgs,
longPressActions: true
})
}
function getType(url){
if(isImageLink(url)){
return "image"
}else if(isVideoLink(url)){
return "video"
}else{
return ''
}
}
function downloadFile(url) {
// 下载所有的文件
let name = url.substring(url.lastIndexOf('/') + 1);
console.log(name, "文件名称");
if (isVideoLink(url) || isImageLink(url)) {
// 是图片和视频
show.value = true;
showIframe.value = true;
iframeUrl.value = url;
} else {
show.value = true;
showIframe.value = false;
iframeUrl.value = url;
}
return;
}
function previewImgOne(img) {
console.log(img);
downloadFile(img.url, Date.now() + ".jpg")
// uni.previewImage({
// current: 0,
// urls: [img.url],
// longPressActions: true
// })
}
function typeIsImg(filename) {
// 获取文件名称是否为图片
const imageExtensions = /\.(jpg|jpeg|png|gif|bmp)$/i;
return imageExtensions.test(filename);
}
function getStatus(item) {
if (item.isFuture) {
return 'waiting'
} else if (item.finishTime === null) {
return 'pending'
} else if (item.nodeType === 'CC') {
return 'cc'
} else if (item.result === 'agree') {
return 'success'
} else if (item.result === 'refuse') {
return 'error'
} else if (item.result === 'comment') {
return 'comment'
} else if (item.result === 'transfer') {
return 'transfer'
} else if (item.result === 'recall') {
return 'recall'
} else if (item.nodeType === 'cancel') {
return 'cancel'
} else {
return undefined
}
}
</script>
<style lang="less" scoped>
.fixed {
position: fixed;
z-index: 9999;
left: 0;
top: 0;
right: 0;
bottom: 0;
background-color: rgba(0, 0, 0, .3);
display: none;
&.show {
display: initial;
}
}
.iframeMain {
position: absolute;
left: 50%;
top: 50%;
transform: translate(-50%, -50%);
width: 650rpx;
font-size: 0;
.iframe {
width: 100%;
height: 500rpx;
}
}
.w-process {
font-size: 32rpx;
.w-process-node {
position: relative;
padding-bottom: 64rpx;
//连接线
.w-process-item-line {
background-color: #E4E4E4;
position: absolute;
width: 6rpx;
height: 100%;
left: 40rpx;
}
//节点体
.w-process-node-content {
margin-left: 96rpx;
}
}
.w-process-item {
display: flex;
position: relative;
.w-process-item-avatar {
padding: 5px 0;
background-color: white;
border-radius: 50%;
}
.w-p-text-info {
display: flex;
flex-direction: column;
justify-content: center;
margin-left: 16rpx;
&>view:first-child {
font-size: 29rpx;
color: #8C8C8C;
}
&>view {
padding: 3rpx;
}
}
.w-p-time {
position: absolute;
color: #8C8C8C;
font-size: 30rpx;
top: 10rpx;
right: 0;
}
}
.w-process-end {
display: flex;
align-items: center;
padding: 3rpx;
&>view:first-child {
display: flex;
background-color: white;
}
.w-pr-running {
width: 80%;
height: 100%;
background-color: #E4E4E4;
border-radius: 50%;
display: flex;
justify-content: center;
align-items: center;
}
.w-pr-status {
width: 80%;
height: 100%;
}
}
}
.w-comments {
margin-top: 10rpx;
padding: 16rpx;
background-color: #F4F5F7;
border-radius: 0 8px 8px 8px;
.w-comment {
image {
margin: 2px;
width: 112rpx;
height: 112rpx;
}
&>view:first-child {
font-size: 29rpx;
color: #515151;
margin-bottom: 6rpx;
}
.ellipsis {
overflow: hidden;
>view {
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
}
}
}
.w-cmt-node {
padding-bottom: 16rpx !important;
}
</style>