初始提交
This commit is contained in:
196
pages/workFlow/components/CandidateForm.vue
Normal file
196
pages/workFlow/components/CandidateForm.vue
Normal file
@@ -0,0 +1,196 @@
|
||||
<template>
|
||||
<view class="candidateForm-v">
|
||||
<u-popup mode="left" :popup="false" v-model="showPopup" length="auto" @close="close" width="100%">
|
||||
<view class="jnpf-wrap jnpf-wrap-form">
|
||||
<u-form :model="candidateForm" ref="candidateForm" :errorType="['toast']" label-position="left"
|
||||
label-width="150" label-align="left">
|
||||
<u-form-item label="分支选择" prop="branch" v-if="candidateType == 1" required>
|
||||
<JnpfSelect v-model="candidateForm.branchList" @change="branchChange" placeholder="请选择审批分支"
|
||||
:options="branchList" :multiple="true" :props='props' />
|
||||
</u-form-item>
|
||||
<u-form-item label-width="250" v-for="(item,index) in candidateForm.list" :key="index"
|
||||
:label="item.label">
|
||||
<u-input type="select" :select-open="item.selectShow" v-model="item.value"
|
||||
@click="openSelect(item)" placeholder="请选择审批候选人">
|
||||
</u-input>
|
||||
</u-form-item>
|
||||
</u-form>
|
||||
<view class="buttom-actions">
|
||||
<u-button class="buttom-btn" @click="cancel">取消</u-button>
|
||||
<u-button class="buttom-btn" type="primary" @click="submit">确定</u-button>
|
||||
</view>
|
||||
</view>
|
||||
</u-popup>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
props: {
|
||||
modelValue: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
formData: {
|
||||
type: Object,
|
||||
default: () => ({})
|
||||
},
|
||||
taskId: {
|
||||
type: String,
|
||||
default: ""
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
showPopup: false,
|
||||
candidateForm: {
|
||||
list: [],
|
||||
branchList: []
|
||||
},
|
||||
nodeId: '',
|
||||
selectId: {},
|
||||
rules: {},
|
||||
selectList: [],
|
||||
selectVal: {},
|
||||
isCandidate: false,
|
||||
props: {
|
||||
label: 'nodeName',
|
||||
value: 'nodeId'
|
||||
},
|
||||
candidateType: 1,
|
||||
branchList: [],
|
||||
candidateList: []
|
||||
};
|
||||
},
|
||||
watch: {
|
||||
modelValue: {
|
||||
immediate: true,
|
||||
handler(val) {
|
||||
this.showPopup = val
|
||||
}
|
||||
},
|
||||
},
|
||||
created() {
|
||||
uni.$on('confirm', (data, id) => {
|
||||
this.selectConfirm(data, id)
|
||||
})
|
||||
},
|
||||
onUnload() {
|
||||
uni.$off('confirm')
|
||||
},
|
||||
methods: {
|
||||
branchChange(e) {
|
||||
this.candidateForm.branchList = e;
|
||||
this.init()
|
||||
},
|
||||
init(candidateType, branchList, candidateList) {
|
||||
this.candidateType = candidateType;
|
||||
this.branchList = branchList;
|
||||
this.candidateList = candidateList
|
||||
if (this.candidateType == 1) {
|
||||
let list = [];
|
||||
for (let i = 0; i < this.candidateForm.branchList.length; i++) {
|
||||
inner: for (let j = 0; j < this.branchList.length; j++) {
|
||||
let o = this.branchList[j]
|
||||
if (o.isCandidates) this.isCandidate = o.isCandidates
|
||||
if (this.candidateForm.branchList[i] === o.nodeId && o.isCandidates) {
|
||||
list.push({
|
||||
...o,
|
||||
label: o.nodeName + '审批人',
|
||||
value: '',
|
||||
selectShow: false
|
||||
})
|
||||
break inner
|
||||
}
|
||||
}
|
||||
this.candidateForm.list = list
|
||||
}
|
||||
} else {
|
||||
if (Array.isArray(this.candidateList) && this.candidateList.length) {
|
||||
this.isCandidate = true
|
||||
this.candidateForm.list = this.candidateList.map(o => ({
|
||||
...o,
|
||||
label: o.nodeName + '审批人',
|
||||
value: '',
|
||||
selectShow: false
|
||||
}))
|
||||
}
|
||||
}
|
||||
},
|
||||
openSelect(item) {
|
||||
this.selectList = []
|
||||
for (let o in this.selectVal) {
|
||||
if (o === item.nodeId) this.selectList = this.selectVal[o]
|
||||
}
|
||||
item.formData = this.formData
|
||||
item.taskId = this.taskId
|
||||
item.selectList = !item.value ? [] : this.selectList
|
||||
uni.navigateTo({
|
||||
url: '/pages/workFlow/candiDateUserSelect/index?data=' + encodeURIComponent(JSON.stringify(
|
||||
item))
|
||||
})
|
||||
},
|
||||
|
||||
selectConfirm(e, id) {
|
||||
let selectData = e;
|
||||
let selectVal = [];
|
||||
let val = [];
|
||||
let selectId = [];
|
||||
let nodeId = '';
|
||||
if (!selectData.length) {
|
||||
delete this.selectVal[id]
|
||||
delete this.selectId[id]
|
||||
for (let i = 0; i < this.candidateForm.list.length; i++) {
|
||||
if (id === this.candidateForm.list[i].nodeId) {
|
||||
this.candidateForm.list[i].value = ""
|
||||
}
|
||||
}
|
||||
}
|
||||
for (let i = 0; i < this.candidateForm.list.length; i++) {
|
||||
for (let o = 0; o < selectData.length; o++) {
|
||||
if (selectData[o].nodeId === this.candidateForm.list[i].nodeId) {
|
||||
nodeId = selectData[o].nodeId
|
||||
val.push(selectData[o].userName)
|
||||
selectVal.push(selectData[o])
|
||||
this.candidateForm.list[i].value = val.join(',')
|
||||
selectId.push(selectData[o].userId)
|
||||
this.$set(this.selectId, selectData[o].nodeId, selectId)
|
||||
}
|
||||
}
|
||||
}
|
||||
this.$set(this.selectVal, nodeId, selectVal)
|
||||
},
|
||||
submit() {
|
||||
const query = {
|
||||
candidateType: this.candidateType,
|
||||
branchList: this.candidateForm.branchList
|
||||
}
|
||||
if (this.isCandidate) {
|
||||
query.candidateList = this.selectId
|
||||
for (let rules of this.candidateForm.list) {
|
||||
if (!rules.value) return this.$u.toast(
|
||||
`${rules.nodeName}不能为空`
|
||||
)
|
||||
}
|
||||
}
|
||||
this.$emit('submitCandidate', query);
|
||||
},
|
||||
cancel() {
|
||||
this.close()
|
||||
},
|
||||
close() {
|
||||
this.$emit('input', false);
|
||||
}
|
||||
|
||||
}
|
||||
};
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
.candidateForm-v {
|
||||
|
||||
.jnpf-wrap,
|
||||
.jnpf-wrap-form {
|
||||
padding: 0;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
92
pages/workFlow/components/ErrorForm.vue
Normal file
92
pages/workFlow/components/ErrorForm.vue
Normal file
@@ -0,0 +1,92 @@
|
||||
<template>
|
||||
<view class="dataForm-v">
|
||||
<u-popup mode="left" :popup="false" v-model="show" length="auto" @close="close" width="100%">
|
||||
<view class="diyTitle u-flex">
|
||||
<uni-icons type="back" size="27" class="uni-btn-icon" @click="black"></uni-icons>
|
||||
<view class="txt">异常处理</view>
|
||||
</view>
|
||||
<view class="jnpf-wrap-form u-p-l-20 u-p-r-20">
|
||||
<u-form :model="errorDataForm" ref="errorDataForm" :errorType="['toast']" label-position="left"
|
||||
:label-width="250" label-align="left">
|
||||
<u-form-item prop="errorDataForm" v-for="(item,index) in list" :key="index" :label="item.nodeName"
|
||||
required>
|
||||
<JnpfUserSelect v-model="errorDataForm.errorRuleUserList[item.nodeCode]" :multiple="true"
|
||||
placeholder="请选择异常处理人" />
|
||||
</u-form-item>
|
||||
</u-form>
|
||||
<view class="buttom-actions">
|
||||
<u-button class="buttom-btn" @click="cancel">取消</u-button>
|
||||
<u-button class="buttom-btn" type="primary" @click="submit">确定</u-button>
|
||||
</view>
|
||||
</view>
|
||||
</u-popup>
|
||||
</view>
|
||||
</template>
|
||||
<script>
|
||||
export default {
|
||||
props: {
|
||||
// 通过双向绑定控制组件的弹出与收起
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
errorDataForm: {
|
||||
errorRuleUserList: {},
|
||||
},
|
||||
list: [],
|
||||
show: false,
|
||||
query: {}
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
init(list, query) {
|
||||
this.show = true
|
||||
this.list = list
|
||||
this.list.map(o => {
|
||||
this.$set(this.errorDataForm.errorRuleUserList, o.nodeCode, [])
|
||||
})
|
||||
this.query = {
|
||||
...query
|
||||
}
|
||||
},
|
||||
submit() {
|
||||
const hasEmptyUserList = Object.keys(this.errorDataForm.errorRuleUserList).some(rules => {
|
||||
return !this.errorDataForm.errorRuleUserList[rules].length;
|
||||
});
|
||||
if (hasEmptyUserList) return this.$u.toast('异常处理人员不能为空');
|
||||
const query = {
|
||||
errorRuleUserList: this.errorDataForm.errorRuleUserList,
|
||||
...this.query
|
||||
}
|
||||
this.$emit('submitErrorForm', query);
|
||||
},
|
||||
cancel() {
|
||||
this.close()
|
||||
},
|
||||
black() {
|
||||
this.close()
|
||||
},
|
||||
close() {
|
||||
this.show = false
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
.dataForm-v {
|
||||
.diyTitle {
|
||||
height: 80rpx;
|
||||
padding: 14rpx 6rpx;
|
||||
text-align: center;
|
||||
justify-content: flex-start;
|
||||
|
||||
.uniui-back {
|
||||
font-size: 27px;
|
||||
font-weight: lighter;
|
||||
}
|
||||
|
||||
.txt {
|
||||
flex: 0.95;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
280
pages/workFlow/components/RecordTimeList/ProcessComments.vue
Normal file
280
pages/workFlow/components/RecordTimeList/ProcessComments.vue
Normal file
@@ -0,0 +1,280 @@
|
||||
<template>
|
||||
<view class="record-v">
|
||||
<view class="discuss_box" v-if="commentList.length">
|
||||
<view class="u-flex-col discuss_list" v-for="(item,index) in commentList" :key="index">
|
||||
<view class="u-flex discuss_txt">
|
||||
<view class="discuss_txt_left u-flex">
|
||||
<u-avatar :src="baseURL+item.creatorUserHeadIcon"></u-avatar>
|
||||
<span class="uName">
|
||||
<span class="comment-header-color">{{item.creatorUser}}</span>
|
||||
<span v-if="item.replyUser">
|
||||
<span class="replyText comment-content-color">回复</span>
|
||||
<span class="replyText comment-header-color">{{ item.replyUser }}</span>
|
||||
<span class="replyText"> <span class="icon-ym icon-ym-chat"
|
||||
@click="openReplyText(item.replyText)"></span></span>
|
||||
</span>
|
||||
</span>
|
||||
</view>
|
||||
</view>
|
||||
<view class="u-flex-col discuss_content">
|
||||
<view class="msg-text">
|
||||
<view v-for="(item2,j) in item.text" :key="j">
|
||||
<text class="txt comment-content-color" v-if="item2.type=='text'">{{item2.content}}</text>
|
||||
<image class="msg-text-emoji" :src="item2.content" v-if="item2.type=='emjio'" />
|
||||
</view>
|
||||
</view>
|
||||
<JnpfUploadImg v-model="item.image" disabled detailed align='left' v-if="item.isDel != 2" />
|
||||
<view v-for='(file,f) in item.file' :key="f" class="jnpf-file-item u-type-primary u-flex u-line-1"
|
||||
@click="openFile(file)">
|
||||
<view class="u-line-1" style="margin-bottom: 10rpx;">
|
||||
{{file.name}}
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<view class="u-flex discuss_txt time_button">
|
||||
<text
|
||||
class="discuss_txt_left u-flex comment-creator-time">{{$u.timeFormat(item.creatorTime,'yyyy-mm-dd hh:MM:ss')}}</text>
|
||||
<view>
|
||||
<text v-if="item.isDel == 1" class="del" @click.stop="delComment(item.id, index)">删除</text>
|
||||
<text v-if="item.isDel != 2" class="reply" @click.stop="handleReply(item.id)">回复</text>
|
||||
</view>
|
||||
</view>
|
||||
<u-divider half-width="100%" :margin-top="32" :margin-bottom="32" :use-slot="false"
|
||||
v-if="index != commentList.length-1" />
|
||||
</view>
|
||||
</view>
|
||||
<JnpfEmpty v-else />
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import {
|
||||
getCommentList,
|
||||
delComment
|
||||
} from '@/api/workFlow/flowEngine'
|
||||
import {
|
||||
getDownloadUrl
|
||||
} from '@/api/common'
|
||||
import {
|
||||
emojiList,
|
||||
imagesMap
|
||||
} from '../../flowBefore/emoji'
|
||||
export default {
|
||||
props: {
|
||||
taskId: {
|
||||
type: [String, Number],
|
||||
default: ''
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
emojiList: emojiList,
|
||||
commentList: []
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
baseURL() {
|
||||
return this.define.baseURL
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
getCommentList() {
|
||||
let query = {
|
||||
currentPage: 1,
|
||||
pageSize: 100000,
|
||||
sort: 'desc',
|
||||
sidx: '',
|
||||
taskId: this.taskId
|
||||
}
|
||||
getCommentList(query).then(res => {
|
||||
this.commentList = [];
|
||||
const list = res.data.list.map((o) => {
|
||||
o.image = JSON.parse(o.image)
|
||||
o.file = JSON.parse(o.file)
|
||||
o.text = this.replaceEmoji(o.text)
|
||||
return o
|
||||
})
|
||||
this.commentList = this.commentList.concat(list);
|
||||
}).catch((err) => {})
|
||||
},
|
||||
openReplyText(replyText) {
|
||||
uni.showModal({
|
||||
content: replyText,
|
||||
showCancel: false,
|
||||
success: res => {}
|
||||
})
|
||||
},
|
||||
replaceEmoji(str) { //替换表情符号为图片
|
||||
if (!str) return ''
|
||||
let replacedStr = str.replace(/\[([^(\]|\[)]*)\]/g, item => 'jnpfjnpf' + item + 'jnpfjnpf');
|
||||
let strArr = replacedStr.split(/jnpfjnpfjnpfjnpf|jnpfjnpf/g)
|
||||
strArr = strArr.filter(o => o)
|
||||
let contentList = []
|
||||
for (let i = 0; i < strArr.length; i++) {
|
||||
let item = {
|
||||
content: strArr[i],
|
||||
type: 'emjio'
|
||||
}
|
||||
if (/\[([^(\]|\[)]*)\]/.test(strArr[i])) {
|
||||
let content = ''
|
||||
for (let j = 0; j < this.emojiList.length; j++) {
|
||||
let row = this.emojiList[j];
|
||||
if (row.alt == strArr[i]) {
|
||||
content = this.getEmojiUrl(row.url)
|
||||
break
|
||||
}
|
||||
}
|
||||
item = {
|
||||
content: content,
|
||||
type: 'emjio'
|
||||
}
|
||||
} else {
|
||||
item = {
|
||||
content: strArr[i],
|
||||
type: 'text'
|
||||
}
|
||||
}
|
||||
contentList.push(item)
|
||||
}
|
||||
return contentList
|
||||
},
|
||||
getEmojiUrl(url) {
|
||||
return imagesMap[url.replace('.', '')]
|
||||
},
|
||||
delComment(id, i) {
|
||||
uni.showModal({
|
||||
title: '提示',
|
||||
content: '确定删除?',
|
||||
success: (res) => {
|
||||
if (res.confirm) {
|
||||
delComment(id).then(res => {
|
||||
this.getCommentList()
|
||||
this.commentList.splice(i, 1)
|
||||
})
|
||||
}
|
||||
}
|
||||
})
|
||||
},
|
||||
handleReply(id) {
|
||||
this.$emit('handleReply', id)
|
||||
},
|
||||
openFile(item) {
|
||||
// #ifdef MP
|
||||
this.previewFile(item)
|
||||
// #endif
|
||||
// #ifdef H5 || APP
|
||||
getDownloadUrl('annex', item.fileId).then(res => {
|
||||
// #ifdef H5
|
||||
window.location.href = this.baseURL + res.data.url + '&name=' + item.name;
|
||||
// #endif
|
||||
// #ifdef APP
|
||||
uni.downloadFile({
|
||||
url: this.baseURL + res.data.url + '&name=' + item.name,
|
||||
success: function(res) {
|
||||
var filePath = res.tempFilePath;
|
||||
uni.openDocument({
|
||||
filePath: filePath,
|
||||
showMenu: true,
|
||||
success: function(res) {}
|
||||
});
|
||||
}
|
||||
});
|
||||
// #endif
|
||||
})
|
||||
// #endif
|
||||
},
|
||||
previewFile(item) {
|
||||
let url = item.url
|
||||
uni.downloadFile({
|
||||
url: this.baseURL + url,
|
||||
success: (res) => {
|
||||
var filePath = res.tempFilePath;
|
||||
uni.openDocument({
|
||||
filePath: encodeURI(filePath),
|
||||
success: (res) => {}
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
// 流程评论end
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
.record-v {
|
||||
height: 100%;
|
||||
background-color: #fff;
|
||||
|
||||
|
||||
.msg-text {
|
||||
border-radius: 4rpx 30rpx 30rpx 30rpx;
|
||||
padding: 16rpx 32rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
font-size: 14px;
|
||||
line-height: 17px;
|
||||
font-family: PingFang SC;
|
||||
word-break: break-word;
|
||||
flex-wrap: wrap;
|
||||
background-color: #fff;
|
||||
|
||||
.msg-text-emoji {
|
||||
vertical-align: top;
|
||||
width: 48rpx;
|
||||
height: 48rpx;
|
||||
display: inline-block;
|
||||
}
|
||||
}
|
||||
|
||||
.discuss_box {
|
||||
padding: 20rpx;
|
||||
|
||||
.discuss_list {
|
||||
|
||||
.time_button {
|
||||
padding-left: 110rpx;
|
||||
margin-top: 20rpx;
|
||||
padding-right: 10rpx;
|
||||
}
|
||||
|
||||
.discuss_txt {
|
||||
width: 100%;
|
||||
justify-content: space-between;
|
||||
|
||||
.discuss_txt_left {
|
||||
.uName {
|
||||
margin-left: 8px;
|
||||
font-size: 14px;
|
||||
font-family: PingFang SC;
|
||||
line-height: 17rpx;
|
||||
|
||||
.replyText {
|
||||
margin-left: 8px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.del {
|
||||
color: red;
|
||||
margin-left: 20px;
|
||||
}
|
||||
|
||||
.reply {
|
||||
margin-left: 20px;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
.discuss_content {
|
||||
font-size: 12px;
|
||||
padding-left: 70rpx;
|
||||
|
||||
.img_box {
|
||||
margin: 40rpx 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
125
pages/workFlow/components/RecordTimeList/TaskLogModal.vue
Normal file
125
pages/workFlow/components/RecordTimeList/TaskLogModal.vue
Normal file
@@ -0,0 +1,125 @@
|
||||
<template>
|
||||
<uni-popup ref="taskLogPoup" background-color="#fff" border-radius="8rpx 8rpx 0 0" :is-mask-click="false">
|
||||
<view class="timeLine-popup-content u-flex-col">
|
||||
<view class="u-flex head-title">
|
||||
<text class="text">任务流程</text>
|
||||
<text class="text icon-ym icon-ym-fail" @click="popupClose"></text>
|
||||
</view>
|
||||
<view class="content" v-for="item,index in taskLogList" :key="index" v-if="taskLogList.length">
|
||||
<view class="u-flex-col content-info-process">
|
||||
<view class="info-process-item">
|
||||
<view class="u-flex u-m-t-20 u-m-b-20 process-item-head">
|
||||
<view class="left">
|
||||
<text>触发时间:</text>
|
||||
<text>{{item.startTime?$u.timeFormat(item.startTime, 'yyyy-mm-dd hh:MM:ss'):''}}</text>
|
||||
</view>
|
||||
<u-tag :text="item.isAsync == 1 ? '异步' : '同步'" size="mini"
|
||||
:type="item.isAsync == 1 ? 'error' : 'primary'" />
|
||||
</view>
|
||||
<view class="process-list u-m-t-14">
|
||||
<view class="u-flex list u-p-l-12 u-p-r-12" v-for="(child,c) in item.recordList" :key="c">
|
||||
<view class="list-left">
|
||||
<u-icon :name="child.status === 0 ? 'checkmark-circle-fill' : 'close-circle-fill'"
|
||||
:color="child.status === 0 ? '#52c41a' : '#f4420a' "></u-icon>
|
||||
<text class="u-m-l-8">{{child.nodeName}}</text>
|
||||
</view>
|
||||
<text class="u-m-l-8 r-txt" v-if="child.status !== 0"
|
||||
@click="jumpErrorPage(child)">查看异常</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<JnpfEmpty v-else />
|
||||
</view>
|
||||
</uni-popup>
|
||||
</template>
|
||||
<script>
|
||||
export default {
|
||||
props: {
|
||||
taskLogList: {
|
||||
type: Array,
|
||||
default: () => []
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {}
|
||||
},
|
||||
computed: {
|
||||
baseURL() {
|
||||
return this.define.baseURL
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
jumpErrorPage(e) {
|
||||
let data = {
|
||||
errorData: e.errorData,
|
||||
errorTip: e.errorTip
|
||||
}
|
||||
uni.navigateTo({
|
||||
url: '/pages/workFlow/flowBefore/logError?data=' + this.jnpf.base64.encode(JSON.stringify(
|
||||
data),
|
||||
"UTF-8"),
|
||||
})
|
||||
},
|
||||
open() {
|
||||
this.$nextTick(() => {
|
||||
this.$refs.taskLogPoup.open('bottom')
|
||||
})
|
||||
},
|
||||
popupClose() {
|
||||
this.$refs.taskLogPoup.close()
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.timeLine-popup-content {
|
||||
padding: 20rpx;
|
||||
height: 1200rpx;
|
||||
overflow-y: scroll;
|
||||
|
||||
.head-title {
|
||||
justify-content: space-between;
|
||||
color: #333333;
|
||||
/* #ifdef APP-PLUS */
|
||||
padding: 20rpx 0;
|
||||
/* #endif */
|
||||
height: 80rpx;
|
||||
}
|
||||
|
||||
.content {
|
||||
.content-info-process {
|
||||
.info-process-item {
|
||||
border-bottom: 1rpx solid #d7d7d7;
|
||||
padding-bottom: 26rpx;
|
||||
|
||||
.process-item-head {
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.process-list {
|
||||
background-color: #f1f4f8;
|
||||
border-radius: 8rpx;
|
||||
|
||||
.list {
|
||||
height: 68rpx;
|
||||
align-items: center;
|
||||
border-bottom: 1rpx solid #e7e9ec;
|
||||
justify-content: space-between;
|
||||
|
||||
.r-txt {
|
||||
color: #0000ff;
|
||||
}
|
||||
|
||||
&:last-child {
|
||||
border-bottom: none
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
300
pages/workFlow/components/RecordTimeList/TimeLine.vue
Normal file
300
pages/workFlow/components/RecordTimeList/TimeLine.vue
Normal file
@@ -0,0 +1,300 @@
|
||||
<template>
|
||||
<view class="u-p-l-20 u-p-r-20 u-p-t-20">
|
||||
<u-time-line>
|
||||
<u-time-line-item nodeTop="2" class="u-p-b-20" v-for="item,index in progressList" :key="index">
|
||||
<template v-slot:node>
|
||||
<view class="u-node" :style="{ background: getTimeLineTagColor(item.nodeStatus) }"></view>
|
||||
</template>
|
||||
<template v-slot:content>
|
||||
<view class="u-font-24 content">
|
||||
<view class="u-order-title u-flex u-m-b-8">
|
||||
<view class="u-line-1 name" v-if="['start', 'end', 'outside'].includes(item.nodeType)">
|
||||
{{item.nodeName}}
|
||||
</view>
|
||||
<view class="u-line-1 name" v-else>
|
||||
{{item.nodeName + getCounterSignContent(item.counterSign, item.assigneeType)}}
|
||||
</view>
|
||||
<view class="u-m-l-10">
|
||||
<!-- 流程状态 -->
|
||||
<u-tag :text="getNodeStatusContent(item.nodeStatus)" mode="light" size="mini"
|
||||
shape="circle" :type="getNodeStatusColor(item.nodeStatus)"
|
||||
v-if="item.nodeType !=='end' && getNodeStatusContent(item.nodeStatus)" />
|
||||
<text
|
||||
class="u-m-l-10">{{item.startTime ? $u.timeFormat(item.startTime, 'mm-dd hh:MM'):''}}</text>
|
||||
</view>
|
||||
</view>
|
||||
<view class="u-flex avatar-box" v-if="item.nodeStatus == 1">
|
||||
<u-avatar :src="baseURL + taskInfo.headIcon" size="mini" mode="circle"
|
||||
class="avatar"></u-avatar>
|
||||
<text class="u-m-l-8">发起人:{{taskInfo.creatorUser}}</text>
|
||||
</view>
|
||||
<view class="u-flex-col approver-list" v-if="item.nodeStatus != 1 && item.approver?.length">
|
||||
<view class="u-flex approver-item" @click="openApprover(item)">
|
||||
<view class="u-flex approver-list-l">
|
||||
<view class="u-flex-col approver-list-l-box" v-for="child,i in item.approver"
|
||||
:key="i">
|
||||
<u-avatar :src="baseURL + child.headIcon" size="mini" mode="circle"
|
||||
class="avatar"></u-avatar>
|
||||
<u-tag :text="useDefine.getFlowStateContent(child.handleType)" mode="light"
|
||||
v-if="useDefine.getFlowStateContent(child.handleType)" class="tag"
|
||||
size="mini"
|
||||
:border-color="useDefine.getHexColor(useDefine.getFlowStateColor(child.handleType))"
|
||||
:bg-color="useDefine.getHexColor(useDefine.getFlowStateColor(child.handleType))"
|
||||
color="#fff" />
|
||||
<text class="u-m-t-20 u-line-1 approver-user-name">
|
||||
{{child.userName}}
|
||||
</text>
|
||||
</view>
|
||||
</view>
|
||||
<view class="u-m-l-20 approver-list-r u-flex">
|
||||
<view class="approver-list-r-box">{{item.approverCount}}</view>
|
||||
<text class="icon-ym icon-ym-right u-m-r-12"></text>
|
||||
</view>
|
||||
</view>
|
||||
<view class="bottom-block" @click="openTaskLogModal(item)" v-if="item.showTaskFlow">
|
||||
<u-tag text="任务流程" type="info" mode="dark" class="u-m-l-10" />
|
||||
<text class="icon-ym icon-ym-right u-m-r-12"></text>
|
||||
</view>
|
||||
</view>
|
||||
<view class="u-flex outside-sign" v-if="item.nodeType == 'outside'">
|
||||
<view>数据传递{{ getOutsideState(item.outSideStatus)}}</view>
|
||||
<view v-if="!item.outSideStatus">
|
||||
<text class="u-m-l-8 r-txt btn-link" @click="handleShowErrorModal(item)">查看异常</text >
|
||||
<text class="u-m-l-8 r-txt btn-link danger" @click="handleRetry(item)" v-if="item.isRetry">重试</text >
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
</u-time-line-item>
|
||||
</u-time-line>
|
||||
</view>
|
||||
<TimeLinePopup :popupTitle="popupTitle" :recordList="recordList" ref="TimeLinePopup">
|
||||
</TimeLinePopup>
|
||||
<TaskLogModal ref="TaskLogModal" :taskLogList="taskLogList" />
|
||||
</template>
|
||||
<script>
|
||||
import {
|
||||
recordList,
|
||||
taskList,
|
||||
retryOutside
|
||||
} from '@/api/workFlow/flowBefore'
|
||||
import fileList from './fileList.vue'
|
||||
import TimeLinePopup from './TimeLinePopup.vue'
|
||||
import TaskLogModal from './TaskLogModal.vue'
|
||||
import {
|
||||
useDefineSetting
|
||||
} from '@/utils/useDefineSetting';
|
||||
export default {
|
||||
components: {
|
||||
fileList,
|
||||
TimeLinePopup,
|
||||
TaskLogModal
|
||||
},
|
||||
props: {
|
||||
progressList: {
|
||||
type: Array,
|
||||
default: () => []
|
||||
},
|
||||
taskInfo: {
|
||||
type: Object,
|
||||
default: () => {}
|
||||
},
|
||||
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
useDefine: useDefineSetting(),
|
||||
recordList: [],
|
||||
popupTitle: '',
|
||||
taskLogList: []
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
baseURL() {
|
||||
return this.define.baseURL
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
getCounterSignContent(counterSign, assigneeType) {
|
||||
if (assigneeType == 10) return '(逐级审批)';
|
||||
return counterSign == 0 ? '(或签)' : counterSign == 1 ? '(会签)' : '(依次审批)';
|
||||
},
|
||||
getNodeStatusColor(status) {
|
||||
return status == 1 || status == 2 ? 'success' : status == 3 ? 'error' : 'primary';
|
||||
},
|
||||
getNodeStatusContent(status) {
|
||||
const list = ['', '已提交', '已通过', '已拒绝', '审批中', '已退回', '已撤回', '等待中', '办理中'];
|
||||
return list[status] || '';
|
||||
},
|
||||
/* 任务流程 */
|
||||
openTaskLogModal(item) {
|
||||
let data = {
|
||||
taskId: this.taskInfo.id,
|
||||
nodeCode: item.nodeId
|
||||
}
|
||||
taskList(data).then(res => {
|
||||
this.taskLogList = res.data || []
|
||||
this.$nextTick(() => {
|
||||
this.$refs.TaskLogModal.open()
|
||||
})
|
||||
})
|
||||
},
|
||||
openApprover(item) {
|
||||
if (item.nodeType === "start" || item.nodeType === "end") return
|
||||
this.popupTitle = item.nodeName + this.getCounterSignContent(item.counterSign, item.assigneeType)
|
||||
this.nodeId = item.nodeId
|
||||
this.getRecordList()
|
||||
},
|
||||
getTimeLineTagColor(status) {
|
||||
return status == 1 || status == 2 ? '#08AF28' : status != 3 ? '#0177FF' : '#ed6f6f';
|
||||
},
|
||||
getRecordList() {
|
||||
let data = {
|
||||
taskId: this.taskInfo.id,
|
||||
nodeId: this.nodeId
|
||||
}
|
||||
recordList(data).then(res => {
|
||||
let list = res.data || []
|
||||
list.map(o => {
|
||||
o.fileList = JSON.parse(o.fileList)
|
||||
})
|
||||
this.recordList = list
|
||||
this.$nextTick(() => {
|
||||
this.$refs.TimeLinePopup.open()
|
||||
})
|
||||
})
|
||||
},
|
||||
getOutsideState(state) {
|
||||
return state ? '成功' : '失败';
|
||||
},
|
||||
handleShowErrorModal(e) {
|
||||
let data = {
|
||||
errorData: e.errorData,
|
||||
errorTip: e.errorTip
|
||||
}
|
||||
uni.navigateTo({
|
||||
url: '/pages/workFlow/flowBefore/logError?data=' + this.jnpf.base64.encode(JSON.stringify(
|
||||
data),
|
||||
"UTF-8"),
|
||||
})
|
||||
},
|
||||
handleRetry(e) {
|
||||
retryOutside(e.nodeId).then((res) => {
|
||||
uni.showToast({
|
||||
title: res.msg,
|
||||
complete: () => {
|
||||
uni.$emit('refresh')
|
||||
},
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
.u-node {
|
||||
width: 20rpx;
|
||||
height: 20rpx;
|
||||
border-radius: 50%;
|
||||
}
|
||||
|
||||
.active {
|
||||
background: #02a7f0;
|
||||
}
|
||||
|
||||
.content {
|
||||
.u-order-title {
|
||||
.name {
|
||||
flex: 1;
|
||||
}
|
||||
}
|
||||
|
||||
.avatar-box {
|
||||
::v-deep .u-avatar {
|
||||
// width: 2rem !important;
|
||||
// height: 2rem !important;
|
||||
}
|
||||
}
|
||||
.outside-sign {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
height: 40px;
|
||||
padding: 0 16px;
|
||||
cursor: pointer;
|
||||
background-color: #F5F5F5;
|
||||
|
||||
.btn-link {
|
||||
background: none;
|
||||
border: none;
|
||||
color: #007AFF;
|
||||
padding: 0;
|
||||
font-size: 28rpx;
|
||||
line-height: 1;
|
||||
}
|
||||
|
||||
.danger {
|
||||
color: #ff0000;
|
||||
padding-left: 12px;
|
||||
}
|
||||
}
|
||||
|
||||
.approver-list {
|
||||
border-radius: 8rpx;
|
||||
min-height: 140rpx;
|
||||
background-color: #F5F5F5;
|
||||
|
||||
.approver-item {
|
||||
.approver-list-l {
|
||||
flex: 1;
|
||||
|
||||
.approver-list-l-box {
|
||||
width: 120rpx;
|
||||
align-items: center;
|
||||
position: relative;
|
||||
|
||||
.tag {
|
||||
position: absolute;
|
||||
top: 50rpx;
|
||||
}
|
||||
|
||||
.approver-user-name {
|
||||
color: #303133;
|
||||
width: 120rpx;
|
||||
text-align: center;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.approver-list-r {
|
||||
height: 4.375rem;
|
||||
|
||||
.approver-list-r-box {
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
margin-right: 10px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
border-radius: 12px;
|
||||
background-color: #fff;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.bottom-block {
|
||||
height: 80rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
border-top: 1rpx solid #e5e5e5;
|
||||
|
||||
:deep(.u-tag) {
|
||||
padding: 8rpx;
|
||||
background-color: rgb(165, 168, 172);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
173
pages/workFlow/components/RecordTimeList/TimeLinePopup.vue
Normal file
173
pages/workFlow/components/RecordTimeList/TimeLinePopup.vue
Normal file
@@ -0,0 +1,173 @@
|
||||
<template>
|
||||
<uni-popup ref="flowStepPopup" background-color="#fff" border-radius="8rpx 8rpx 0 0" :is-mask-click="false">
|
||||
<view class="timeLine-popup-content u-flex-col">
|
||||
<view class="u-flex head-title">
|
||||
<text class="text">{{popupTitle}}</text>
|
||||
<text class="text icon-ym icon-ym-fail" @click="popupClose"></text>
|
||||
</view>
|
||||
<view class="content" v-for="item,index in recordList" :key="index" v-if="recordList.length">
|
||||
<!--头部 -->
|
||||
<view class="u-flex u-m-t-20 content-info">
|
||||
<view class="u-flex content-info-left">
|
||||
<u-avatar :src="baseURL + item.headIcon" size="mini" mode="circle" class="avatar"></u-avatar>
|
||||
<view class="u-m-l-10 name-box">
|
||||
<p>{{item.userName}}</p>
|
||||
<p class="name">
|
||||
{{item.handleTime?$u.timeFormat(item.handleTime, 'yyyy-mm-dd hh:MM:ss'):''}}
|
||||
</p>
|
||||
</view>
|
||||
</view>
|
||||
<u-tag :text="useDefine.getFlowStateContent(item.handleType)"
|
||||
:border-color="useDefine.getHexColor(useDefine.getFlowStateColor(item.handleType))"
|
||||
:bg-color="useDefine.getHexColor(useDefine.getFlowStateColor(item.handleType))" color="#fff"
|
||||
size="mini" shape="circle" />
|
||||
<view class="content-info-right u-line-1" v-if="item.handleUserName">
|
||||
<u-icon name="arrow-rightward" color="#1890ff" class="u-m-l-10 u-m-r-10"></u-icon>
|
||||
<text class="u-font-24 txt u-line-1">{{item.handleUserName}}</text>
|
||||
</view>
|
||||
</view>
|
||||
<view class="content-info-bottom" v-if="item.signImg || item.fileList?.length || item.handleOpinion">
|
||||
<text class="u-line-2" v-if="item.handleOpinion">{{item.handleOpinion}}</text>
|
||||
<fileList :fileList="item.fileList" v-if="item?.fileList?.length"></fileList>
|
||||
<view class="u-flex sign-box" v-if="item.signImg">
|
||||
<view class="sign-title">签名:</view>
|
||||
<JnpfSign v-model="item.signImg" align="left" detailed />
|
||||
</view>
|
||||
<view class="approvalField" v-for="(field,index) in item.approvalField" :key="index">
|
||||
<text>{{field.fieldName+':'}}</text>
|
||||
<text>{{field.value}}</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<JnpfEmpty v-else />
|
||||
</view>
|
||||
</uni-popup>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import fileList from './fileList.vue'
|
||||
import {
|
||||
useDefineSetting
|
||||
} from '@/utils/useDefineSetting';
|
||||
export default {
|
||||
components: {
|
||||
fileList
|
||||
},
|
||||
props: {
|
||||
recordList: {
|
||||
type: Array,
|
||||
default: () => []
|
||||
},
|
||||
popupTitle: {
|
||||
type: String,
|
||||
default: ''
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
useDefine: useDefineSetting(),
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
baseURL() {
|
||||
return this.define.baseURL
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
open() {
|
||||
this.$nextTick(() => {
|
||||
this.$refs.flowStepPopup.open('bottom')
|
||||
})
|
||||
},
|
||||
popupClose() {
|
||||
this.$refs.flowStepPopup.close()
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.timeLine-popup-content {
|
||||
padding: 20rpx;
|
||||
height: 1200rpx;
|
||||
overflow-y: scroll;
|
||||
|
||||
.head-title {
|
||||
justify-content: space-between;
|
||||
color: #333333;
|
||||
/* #ifdef APP-PLUS */
|
||||
padding: 20rpx 0;
|
||||
/* #endif */
|
||||
}
|
||||
|
||||
.content {
|
||||
.content-info {
|
||||
height: 100rpx;
|
||||
|
||||
.content-info-left {
|
||||
flex: 1;
|
||||
|
||||
.name-box {
|
||||
.name {
|
||||
color: #606266;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.content-info-right {
|
||||
max-width: 228rpx;
|
||||
|
||||
.txt {
|
||||
color: #303133;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.content-info-bottom {
|
||||
background-color: #F5F5F5;
|
||||
padding: 20rpx;
|
||||
border-radius: 8rpx;
|
||||
color: #303133;
|
||||
|
||||
.approvalField {
|
||||
padding: 10rpx 0;
|
||||
}
|
||||
|
||||
.sign-box {
|
||||
height: 88rpx;
|
||||
|
||||
.sign-title {
|
||||
width: 120rpx;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.content-info-process {
|
||||
.info-process-item {
|
||||
border-bottom: 1rpx solid #d7d7d7;
|
||||
padding-bottom: 26rpx;
|
||||
|
||||
.process-item-head {
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.process-list {
|
||||
background-color: #f1f4f8;
|
||||
border-radius: 8rpx;
|
||||
|
||||
.list {
|
||||
height: 68rpx;
|
||||
align-items: center;
|
||||
border-bottom: 1rpx solid #e7e9ec;
|
||||
justify-content: space-between;
|
||||
|
||||
.r-txt {
|
||||
color: #0000ff;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
125
pages/workFlow/components/RecordTimeList/fileList.vue
Normal file
125
pages/workFlow/components/RecordTimeList/fileList.vue
Normal file
@@ -0,0 +1,125 @@
|
||||
<template>
|
||||
<view class="" style="padding: 20rpx 0;">
|
||||
<view v-for='(item,index) in fileList' :key="index"
|
||||
class="jnpf-file-item u-type-primary u-flex u-line-1 u-m-t-10" @tap='downLoad(item)'>
|
||||
<view class="jnpf-file-item-txt u-line-1">{{item.name}}</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
<script>
|
||||
const imgTypeList = ['png', 'jpg', 'jpeg', 'bmp', 'gif']
|
||||
import {
|
||||
getDownloadUrl
|
||||
} from '@/api/common'
|
||||
import jnpf from '@/utils/jnpf'
|
||||
export default {
|
||||
props: {
|
||||
fileList: {
|
||||
type: Array,
|
||||
default: () => []
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
baseURL() {
|
||||
return this.define.baseURL
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
downLoad(item) {
|
||||
if (item.fileExtension && imgTypeList.includes(item.fileExtension)) return this.previewImage(item)
|
||||
// #ifdef MP
|
||||
this.previewFile(item)
|
||||
// #endif
|
||||
// #ifndef MP
|
||||
getDownloadUrl('annex', item.fileId).then(res => {
|
||||
const fileUrl = this.baseURL + res.data.url + '&name=' + item.name;
|
||||
// #ifdef H5
|
||||
window.location.href = fileUrl;
|
||||
// #endif
|
||||
// #ifdef APP-PLUS
|
||||
this.downloadFile(res.data.url);
|
||||
// #endif
|
||||
})
|
||||
// #endif
|
||||
},
|
||||
previewFile(item) {
|
||||
let fileTypes = ['doc', 'xls', 'ppt', 'pdf', 'docx', 'xlsx', 'pptx']
|
||||
let url = item.url
|
||||
let fileType = url.split('.')[1]
|
||||
if (fileTypes.includes(fileType)) {
|
||||
uni.downloadFile({
|
||||
url: this.baseURL + url,
|
||||
success: (res) => {
|
||||
var filePath = res.tempFilePath;
|
||||
uni.openDocument({
|
||||
filePath: encodeURI(filePath),
|
||||
showMenu: true,
|
||||
fileType: fileType,
|
||||
success: (res) => {
|
||||
console.log('打开文档成功');
|
||||
},
|
||||
fail(err) {
|
||||
console.log('小程序', err);
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
} else {
|
||||
this.$u.toast(
|
||||
'该文件类型无法打开'
|
||||
)
|
||||
}
|
||||
},
|
||||
previewImage(item) {
|
||||
if (!item.url) return
|
||||
const url = jnpf.getAuthImgUrl(item.url)
|
||||
uni.previewImage({
|
||||
urls: [url],
|
||||
current: url,
|
||||
success: () => {},
|
||||
fail: () => {
|
||||
uni.showToast({
|
||||
title: '预览图片失败',
|
||||
icon: 'none'
|
||||
});
|
||||
}
|
||||
});
|
||||
},
|
||||
downloadFile(url) {
|
||||
uni.downloadFile({
|
||||
url: this.baseURL + url,
|
||||
success: res => {
|
||||
if (res.statusCode === 200) {
|
||||
uni.saveFile({
|
||||
tempFilePath: res.tempFilePath,
|
||||
success: red => {
|
||||
uni.showToast({
|
||||
icon: 'none',
|
||||
mask: true,
|
||||
title: '文件已保存:' + red.savedFilePath, //保存路径
|
||||
duration: 3000,
|
||||
});
|
||||
setTimeout(() => {
|
||||
uni.openDocument({
|
||||
filePath: red.savedFilePath,
|
||||
success: ress => {},
|
||||
fail(err) {}
|
||||
});
|
||||
}, 500)
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
},
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style>
|
||||
</style>
|
||||
228
pages/workFlow/components/RecordTimeList/index.vue
Normal file
228
pages/workFlow/components/RecordTimeList/index.vue
Normal file
@@ -0,0 +1,228 @@
|
||||
<template>
|
||||
<view class="flowStep u-m-t-20">
|
||||
<view class="tabsContent">
|
||||
<u-tabs :list="getList" :current="current" @change="change"></u-tabs>
|
||||
<view class="time-line-box" v-if="opType != '-1'">
|
||||
<TimeLine :progressList="progressList" :taskInfo="taskInfo" v-if="tabId == 0"></TimeLine>
|
||||
<ProcessComments ref="ProcessComments" v-if="tabId == 1" :taskId="taskId" @handleReply="handleReply">
|
||||
</ProcessComments>
|
||||
<view class="u-p-l-20 u-p-r-20" v-if="tabId == 2">
|
||||
<dataLog :dataLogList="dataLogList"></dataLog>
|
||||
</view>
|
||||
<assistantMsg v-if="tabId == 3" :auxiliaryInfo="auxiliaryInfo" :formData="formData"></assistantMsg>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
<script>
|
||||
import TimeLine from './TimeLine.vue'
|
||||
import dataLog from '@/components/dataLog'
|
||||
import ProcessComments from './ProcessComments.vue'
|
||||
import assistantMsg from '@/components/assistantMsg'
|
||||
export default {
|
||||
components: {
|
||||
ProcessComments,
|
||||
TimeLine,
|
||||
dataLog,
|
||||
assistantMsg
|
||||
},
|
||||
props: {
|
||||
dataLogList: {
|
||||
type: Array,
|
||||
default: () => []
|
||||
},
|
||||
progressList: {
|
||||
type: Array,
|
||||
default: () => []
|
||||
},
|
||||
commentList: {
|
||||
type: Array,
|
||||
default: () => []
|
||||
},
|
||||
taskInfo: {
|
||||
type: Object,
|
||||
default: () => {}
|
||||
},
|
||||
formData: {
|
||||
type: Object,
|
||||
default: () => {}
|
||||
},
|
||||
auxiliaryInfo: {
|
||||
type: Array,
|
||||
default: () => []
|
||||
},
|
||||
taskId: {
|
||||
type: [String, Number],
|
||||
default: ''
|
||||
},
|
||||
hasComment: {
|
||||
default: false
|
||||
},
|
||||
dataLog: {
|
||||
default: false
|
||||
},
|
||||
formID: {
|
||||
type: [String, Number],
|
||||
default: ''
|
||||
},
|
||||
opType: {
|
||||
type: [String, Number],
|
||||
default: ''
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
current: 0,
|
||||
value: '',
|
||||
popupTitle: '',
|
||||
nodeId: '',
|
||||
tabId: 0
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
baseURL() {
|
||||
return this.define.baseURL
|
||||
},
|
||||
getList() {
|
||||
const list = [];
|
||||
const OP_TYPE_INVALID = '-1';
|
||||
if (this.opType !== OP_TYPE_INVALID) {
|
||||
list.push({
|
||||
name: '流转',
|
||||
id: 0
|
||||
});
|
||||
if (this.hasComment) {
|
||||
list.push({
|
||||
name: '评论',
|
||||
id: 1
|
||||
});
|
||||
};
|
||||
if (this.dataLog) {
|
||||
list.push({
|
||||
name: '修改记录',
|
||||
id: 2
|
||||
})
|
||||
}
|
||||
}
|
||||
if (this.auxiliaryInfo.length) {
|
||||
list.push({
|
||||
name: '辅助信息',
|
||||
id: 3
|
||||
});
|
||||
}
|
||||
return list
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
handleReply(id) {
|
||||
this.$emit('handleReply', id)
|
||||
},
|
||||
popupClose() {
|
||||
this.$refs.flowStepPopup.close()
|
||||
},
|
||||
change(e) {
|
||||
this.current = e
|
||||
this.tabId = this.getList[e].id
|
||||
if (this.current === 1) {
|
||||
this.$nextTick(() => {
|
||||
this.$refs.ProcessComments.getCommentList()
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
.flowStep {
|
||||
width: 100%;
|
||||
|
||||
.tabsContent {
|
||||
.time-line-box {
|
||||
background-color: #fff;
|
||||
}
|
||||
|
||||
.bottom-btn {
|
||||
background-color: #fff;
|
||||
width: 100%;
|
||||
height: 88rpx;
|
||||
padding: 0 20rpx;
|
||||
color: #7f7f7f;
|
||||
|
||||
.btn {
|
||||
background-color: #f2f2f2;
|
||||
border-radius: 8rpx;
|
||||
height: 60rpx;
|
||||
padding-left: 10rpx;
|
||||
width: 100%;
|
||||
line-height: 60rpx;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.timeLine {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
padding: 20rpx;
|
||||
|
||||
.u-time-axis-item {
|
||||
.u-time-axis-node {
|
||||
top: 0 !important;
|
||||
}
|
||||
}
|
||||
|
||||
.timeLine-right {
|
||||
padding-left: 0;
|
||||
padding-right: 40rpx !important;
|
||||
|
||||
&::before {
|
||||
left: 670rpx !important;
|
||||
}
|
||||
|
||||
.u-time-axis-item {
|
||||
.u-time-axis-node {
|
||||
left: 670rpx !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.timeLine-dot {
|
||||
width: 28rpx;
|
||||
height: 28rpx;
|
||||
border: 4rpx solid #FFFFFF;
|
||||
box-shadow: 0 6rpx 12rpx rgba(2, 7, 28, 0.16);
|
||||
border-radius: 50%;
|
||||
}
|
||||
|
||||
.timeLine-content {
|
||||
padding: 0 10rpx;
|
||||
|
||||
.timeLine-title {
|
||||
font-size: 30rpx;
|
||||
line-height: 36rpx;
|
||||
|
||||
.name {
|
||||
margin-bottom: 6rpx;
|
||||
}
|
||||
}
|
||||
|
||||
.timeLine-title2 {
|
||||
margin-top: 6rpx;
|
||||
background: rgba(255, 255, 255, 0.39);
|
||||
box-shadow: 0px 3px 10px rgba(0, 0, 0, 0.1);
|
||||
padding: 10rpx 20rpx;
|
||||
border-radius: 4px;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
.timeLine-desc {
|
||||
margin-top: 10rpx;
|
||||
font-size: 26rpx;
|
||||
line-height: 36rpx;
|
||||
color: #909399;
|
||||
margin-bottom: 10rpx;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
126
pages/workFlow/components/fileList.vue
Normal file
126
pages/workFlow/components/fileList.vue
Normal file
@@ -0,0 +1,126 @@
|
||||
<template>
|
||||
<view class="" style="padding: 20rpx 0;">
|
||||
<view v-for='(item,index) in fileList' :key="index"
|
||||
class="jnpf-file-item u-type-primary u-flex u-line-1 u-m-t-10" @tap='downLoad(item)'>
|
||||
<view class="jnpf-file-item-txt u-line-1">{{item.name}}</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
const imgTypeList = ['png', 'jpg', 'jpeg', 'bmp', 'gif']
|
||||
import {
|
||||
getDownloadUrl
|
||||
} from '@/api/common'
|
||||
import jnpf from '@/utils/jnpf'
|
||||
export default {
|
||||
props: {
|
||||
fileList: {
|
||||
type: Array,
|
||||
default: () => []
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
baseURL() {
|
||||
return this.define.baseURL
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
downLoad(item) {
|
||||
if (item.fileExtension && imgTypeList.includes(item.fileExtension)) return this.previewImage(item)
|
||||
// #ifdef MP
|
||||
this.previewFile(item)
|
||||
// #endif
|
||||
// #ifndef MP
|
||||
getDownloadUrl('annex', item.fileId).then(res => {
|
||||
const fileUrl = this.baseURL + res.data.url + '&name=' + item.name;
|
||||
// #ifdef H5
|
||||
window.location.href = fileUrl;
|
||||
// #endif
|
||||
// #ifdef APP-PLUS
|
||||
this.downloadFile(res.data.url);
|
||||
// #endif
|
||||
})
|
||||
// #endif
|
||||
},
|
||||
previewFile(item) {
|
||||
let fileTypes = ['doc', 'xls', 'ppt', 'pdf', 'docx', 'xlsx', 'pptx']
|
||||
let url = item.url
|
||||
let fileType = url.split('.')[1]
|
||||
if (fileTypes.includes(fileType)) {
|
||||
uni.downloadFile({
|
||||
url: this.baseURL + url,
|
||||
success: (res) => {
|
||||
var filePath = res.tempFilePath;
|
||||
uni.openDocument({
|
||||
filePath: encodeURI(filePath),
|
||||
showMenu: true,
|
||||
fileType: fileType,
|
||||
success: (res) => {
|
||||
console.log('打开文档成功');
|
||||
},
|
||||
fail(err) {
|
||||
console.log('小程序', err);
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
} else {
|
||||
this.$u.toast(
|
||||
'该文件类型无法打开'
|
||||
)
|
||||
}
|
||||
},
|
||||
previewImage(item) {
|
||||
if (!item.url) return
|
||||
const url = jnpf.getAuthImgUrl(item.url)
|
||||
uni.previewImage({
|
||||
urls: [url],
|
||||
current: url,
|
||||
success: () => {},
|
||||
fail: () => {
|
||||
uni.showToast({
|
||||
title: '预览图片失败',
|
||||
icon: 'none'
|
||||
});
|
||||
}
|
||||
});
|
||||
},
|
||||
downloadFile(url) {
|
||||
uni.downloadFile({
|
||||
url: this.baseURL + url,
|
||||
success: res => {
|
||||
if (res.statusCode === 200) {
|
||||
uni.saveFile({
|
||||
tempFilePath: res.tempFilePath,
|
||||
success: red => {
|
||||
uni.showToast({
|
||||
icon: 'none',
|
||||
mask: true,
|
||||
title: '文件已保存:' + red.savedFilePath, //保存路径
|
||||
duration: 3000,
|
||||
});
|
||||
setTimeout(() => {
|
||||
uni.openDocument({
|
||||
filePath: red.savedFilePath,
|
||||
success: ress => {},
|
||||
fail(err) {}
|
||||
});
|
||||
}, 500)
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
},
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style>
|
||||
</style>
|
||||
150
pages/workFlow/components/flowBtn.vue
Normal file
150
pages/workFlow/components/flowBtn.vue
Normal file
@@ -0,0 +1,150 @@
|
||||
<template>
|
||||
<view class="flowBefore-actions" :style="{'height': '88rpx'}"
|
||||
v-if="hasComment || (actionList.length || rightBtnList.length) ">
|
||||
<CustomButton v-if="showMoreBtn" btnText="更多" btnType="more" iconName="more-dot-fill" size="28"
|
||||
@handleBtn="showAction = $event" :btnLoading="loading" class="u-flex-col buttom-btn-left-inner" />
|
||||
<template v-if="opType != 2">
|
||||
<CustomButton v-if="showSaveBtn" :btnText="saveBtnText || '暂存'" customIcon
|
||||
class="u-flex-col buttom-btn-left-inner" btnIcon="icon-ym icon-ym-extend-save" size="28"
|
||||
@handleBtn="handleBtn('save')" btnType="save" :btnLoading="loading" />
|
||||
<CustomButton v-if="showRejectBtn" btnText="拒绝" iconName="minus-circle" size="28"
|
||||
@handleBtn="handleBtn('reject')" btnType="reject" :btnLoading="loading"
|
||||
class="u-flex-col buttom-btn-left-inner" />
|
||||
</template>
|
||||
<view class="rightBtn u-flex">
|
||||
<u-button class="buttom-btn" @click.stop="handleBtn('comment')" v-if="!rightBtnList.length"
|
||||
:loading="loading">
|
||||
评论
|
||||
</u-button>
|
||||
<template v-if="opType != 2">
|
||||
<u-button v-for="btn,index in rightBtnList" :key="index" class="buttom-btn"
|
||||
@click.stop="handleBtn(btn.id)" :style="{'width':btn.width}"
|
||||
:class="{'delegate-submit-btn':btn.id === 'delegateSubmit'}" :loading="loading" :type="btn?.type">
|
||||
{{ btn.fullName}}
|
||||
</u-button>
|
||||
</template>
|
||||
<template class="" v-else>
|
||||
<u-button v-for="btn,index in todoBtnList" :key="index" class="buttom-btn"
|
||||
@click.stop="handleBtn(btn.id)" :style="{'width':btn.width}" :loading="loading" :type="btn?.type">
|
||||
{{ btn.fullName}}
|
||||
</u-button>
|
||||
</template>
|
||||
</view>
|
||||
<u-action-sheet v-model="showAction" :list="actionList" @click="handleAction" />
|
||||
</view>
|
||||
</template>
|
||||
<script>
|
||||
import CustomButton from '@/components/CustomButton'
|
||||
export default {
|
||||
components: {
|
||||
CustomButton
|
||||
},
|
||||
props: {
|
||||
actionList: {
|
||||
type: Array,
|
||||
default: () => ([])
|
||||
},
|
||||
rightBtnList: {
|
||||
type: Array,
|
||||
default: () => ([])
|
||||
},
|
||||
todoBtnList: {
|
||||
type: Array,
|
||||
default: () => ([])
|
||||
},
|
||||
btnInfo: {
|
||||
type: Object,
|
||||
default: () => ({})
|
||||
},
|
||||
opType: {
|
||||
type: [String, Number],
|
||||
default: ''
|
||||
},
|
||||
saveBtnText: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
btnLoading: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
hideSaveBtn: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
hasSignFor: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
hasComment: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
isProcessing: {
|
||||
type: [String, Number],
|
||||
default: ''
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
loading() {
|
||||
return this.btnLoading
|
||||
},
|
||||
showMoreBtn() {
|
||||
if (this.actionList.length) return true
|
||||
return false
|
||||
},
|
||||
showRejectBtn() {
|
||||
if (this.btnInfo?.hasRejectBtn && !this.isProcessing && this.opType != 2) return true
|
||||
return false
|
||||
},
|
||||
showSaveBtn() {
|
||||
if (this.btnInfo?.hasSaveBtn && !this.hideSaveBtn) return true
|
||||
return false
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
showAction: false
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
handleBtn(type, item) {
|
||||
if (!this.loading) this.$emit('handleBtn', type, item)
|
||||
},
|
||||
//更多按钮
|
||||
handleAction(index) {
|
||||
this.handleBtn(this.actionList[index].id, this.actionList[index])
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
.flowBefore-actions {
|
||||
.poup-btn {
|
||||
background-color: #fff;
|
||||
width: 100%;
|
||||
height: 88rpx;
|
||||
padding: 0 20rpx;
|
||||
color: #7f7f7f;
|
||||
border-bottom: 1rpx solid #d7d7d7;
|
||||
|
||||
.btn {
|
||||
background-color: #f2f2f2;
|
||||
border-radius: 8rpx;
|
||||
height: 60rpx;
|
||||
padding-left: 10rpx;
|
||||
width: 100%;
|
||||
line-height: 60rpx;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.delegate-submit-btn {
|
||||
background-color: #50abf6;
|
||||
|
||||
button {
|
||||
background-color: #50abf6 !important;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
372
pages/workFlow/components/flowStep.vue
Normal file
372
pages/workFlow/components/flowStep.vue
Normal file
@@ -0,0 +1,372 @@
|
||||
<template>
|
||||
<view class="flowStep u-m-t-20">
|
||||
<view class="tabsContent">
|
||||
<u-tabs :list="list" :current="current" @change="change"></u-tabs>
|
||||
<view class="u-p-l-20 u-p-r-20 time-line-box">
|
||||
<view class="u-p-l-20 u-p-r-20 u-p-t-20" v-if="current == 0">
|
||||
<TimeLine :progressList="progressList" :taskInfo="taskInfo"></TimeLine>
|
||||
<!-- <u-time-line>
|
||||
<u-time-line-item nodeTop="2" class="u-p-b-20" v-for="item,index in progressList" :key="index">
|
||||
<template v-slot:node>
|
||||
<view class="u-node"></view>
|
||||
</template>
|
||||
<template v-slot:content>
|
||||
<view class="u-font-24 content">
|
||||
<view class="u-order-title u-flex u-m-b-8">
|
||||
<view class="u-line-1 name">
|
||||
{{item.nodeName + getCounterSignContent(item.counterSign)}}
|
||||
</view>
|
||||
<view class="u-m-l-10">
|
||||
<u-tag :text="getNodeStatusContent(item.nodeStatus)" mode="light"
|
||||
size="mini" shape="circle"
|
||||
:type="getNodeStatusColor(item.nodeStatus)" />
|
||||
<text
|
||||
class="u-m-l-10">{{item.startTime?$u.timeFormat(item.startTime, 'mm-dd hh:MM'):''}}</text>
|
||||
</view>
|
||||
</view>
|
||||
<view class="u-flex avatar-box" v-if="item.nodeStatus == 1">
|
||||
<u-avatar :src="baseURL + taskInfo.headIcon" size="mini" mode="circle"
|
||||
class="avatar"></u-avatar>
|
||||
<text class="u-m-l-8">发起人:{{taskInfo.creatorUser}}</text>
|
||||
</view>
|
||||
<view class="u-flex approver-list" v-else>
|
||||
<view class="u-flex approver-list-l">
|
||||
<view class="u-flex-col approver-list-l-box"
|
||||
v-for="child,i in item.approver" :key="i">
|
||||
<u-avatar :src="baseURL + taskInfo.headIcon" size="mini" mode="circle"
|
||||
class="avatar"></u-avatar>
|
||||
<u-tag :text="useDefine.getFlowStateContent(child.handleType)"
|
||||
mode="light" v-if="useDefine.getFlowStateContent(child.handleType)"
|
||||
class="tag" size="mini"
|
||||
:border-color="useDefine.getHexColor(useDefine.getFlowStateColor(child.handleType))"
|
||||
:bg-color="useDefine.getHexColor(useDefine.getFlowStateColor(child.handleType))"
|
||||
color="#fff" />
|
||||
<text class="u-m-t-20 u-line-1 approver-user-name">
|
||||
{{child.userName}}
|
||||
</text>
|
||||
</view>
|
||||
</view>
|
||||
<view class="u-m-l-20 approver-list-r u-flex" @click="openApprover(item)">
|
||||
<view class="approver-list-r-box">{{item.approverCount}}</view>
|
||||
<text class="icon-ym icon-ym-right u-m-r-12"></text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
</u-time-line-item>
|
||||
</u-time-line> -->
|
||||
</view>
|
||||
<view class="u-p-l-20 u-p-r-20" v-if="current == 1">
|
||||
2
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<uni-popup ref="flowStepPopup" background-color="#fff" border-radius="8rpx 8rpx 0 0" :is-mask-click="false">
|
||||
<view class="flow-popup-content" style="height: 1200rpx;">
|
||||
<view class="u-flex head-title">
|
||||
<text class="text">{{popupTitle}}</text>
|
||||
<text class="text icon-ym icon-ym-fail" @click="popupClose"></text>
|
||||
</view>
|
||||
<view class="" v-for="item,index in recordList" :key="index" v-if="recordList.length">
|
||||
<view class="u-flex u-m-t-20" style="height: 100rpx;">
|
||||
<view class="u-flex" style="flex: 1;">
|
||||
<u-avatar :src="baseURL + taskInfo.headIcon" size="mini" mode="circle"
|
||||
class="avatar"></u-avatar>
|
||||
<view class="u-m-l-10">
|
||||
<p>{{item.userName}}</p>
|
||||
<p style="color: #606266;">
|
||||
{{item.handleTime?$u.timeFormat(item.handleTime, 'yyyy-mm-dd hh:MM:ss'):''}}
|
||||
</p>
|
||||
</view>
|
||||
</view>
|
||||
<u-tag :text="useDefine.getFlowStateContent(item.handleType)"
|
||||
:border-color="useDefine.getHexColor(useDefine.getFlowStateColor(item.handleType))"
|
||||
:bg-color="useDefine.getHexColor(useDefine.getFlowStateColor(item.handleType))" color="#fff"
|
||||
size="mini" shape="circle" />
|
||||
<view class="" v-if="item.handleUserName">
|
||||
<u-icon name="arrow-rightward" color="#1890ff" class="u-m-l-10 u-m-r-10"></u-icon>
|
||||
<text class="u-font-24" style="color: #303133;">{{item.handleUserName}}</text>
|
||||
</view>
|
||||
</view>
|
||||
<view class="" style="background-color: #F5F5F5;padding: 20rpx;border-radius: 8rpx;color: #303133;"
|
||||
v-if="item.signImg || item.fileList.length || item.handleOpinion">
|
||||
<text class="u-line-2" v-if="item.handleOpinion">{{item.handleOpinion}}</text>
|
||||
<fileList :fileList="item.fileList" v-if="item.fileList.length"></fileList>
|
||||
<view class="u-flex" style="height: 88rpx;" v-if="item.signImg">
|
||||
<view style="width: 60px;">签名:</view>
|
||||
<JnpfSign v-model="item.signImg" align="left" detailed />
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<JnpfEmpty v-else />
|
||||
</view>
|
||||
</uni-popup>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import fileList from './fileList.vue'
|
||||
import TimeLine from './TimeLine.vue'
|
||||
import {
|
||||
useDefineSetting
|
||||
} from '@/utils/useDefineSetting';
|
||||
import {
|
||||
recordList
|
||||
} from '@/api/workFlow/flowBefore'
|
||||
const imgTypeList = ['png', 'jpg', 'jpeg', 'bmp', 'gif']
|
||||
export default {
|
||||
components: {
|
||||
fileList,
|
||||
TimeLine
|
||||
},
|
||||
props: {
|
||||
progressList: {
|
||||
type: Array,
|
||||
default: () => []
|
||||
},
|
||||
taskInfo: {
|
||||
type: Object,
|
||||
default: () => {}
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
list: [{
|
||||
name: '流转'
|
||||
}, {
|
||||
name: '评论'
|
||||
}],
|
||||
current: 0,
|
||||
value: '',
|
||||
popupTitle: '',
|
||||
recordList: [],
|
||||
nodeId: '',
|
||||
useDefine: useDefineSetting()
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
baseURL() {
|
||||
return this.define.baseURL
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
openApprover(item) {
|
||||
this.popupTitle = item.nodeName + this.getCounterSignContent(item.counterSign, item.assigneeType)
|
||||
this.nodeId = item.nodeId
|
||||
this.$nextTick(() => {
|
||||
this.$refs.flowStepPopup.open('bottom')
|
||||
this.getRecordList()
|
||||
})
|
||||
},
|
||||
getRecordList() {
|
||||
let data = {
|
||||
taskId: this.taskInfo.id,
|
||||
nodeId: this.nodeId
|
||||
}
|
||||
recordList(data).then(res => {
|
||||
let list = res.data || []
|
||||
list.map(o => {
|
||||
o.fileList = JSON.parse(o.fileList)
|
||||
})
|
||||
this.recordList = list
|
||||
})
|
||||
},
|
||||
getCounterSignContent(counterSign, assigneeType) {
|
||||
if (assigneeType == 10) return '(逐级审批)';
|
||||
return counterSign == 0 ? '(或签)' : counterSign == 1 ? '(会签)' : '(依次审批)';
|
||||
},
|
||||
popupClose() {
|
||||
this.$refs.flowStepPopup.close()
|
||||
},
|
||||
getNodeStatusContent(status) {
|
||||
const list = ['', '已提交', '已通过', '已拒绝', '审批中'];
|
||||
return list[status] || '';
|
||||
},
|
||||
|
||||
getNodeStatusColor(status) {
|
||||
return status == 1 || status == 2 ? 'success' : status == 3 ? 'error' : 'primary';
|
||||
},
|
||||
change(e) {
|
||||
this.current = e
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
.flow-popup-content {
|
||||
padding: 20rpx;
|
||||
|
||||
.head-title {
|
||||
|
||||
justify-content: space-between;
|
||||
color: #333333;
|
||||
// border-bottom: 1rpx solid #f0f0f0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
.flowStep {
|
||||
width: 100%;
|
||||
|
||||
.tabsContent {
|
||||
.time-line-box {
|
||||
background-color: #fff;
|
||||
|
||||
.u-node {
|
||||
background: #19be6b;
|
||||
width: 20rpx;
|
||||
height: 20rpx;
|
||||
border-radius: 50%;
|
||||
}
|
||||
|
||||
.active {
|
||||
background: #02a7f0;
|
||||
}
|
||||
|
||||
.content {
|
||||
.u-order-title {
|
||||
.name {
|
||||
flex: 1;
|
||||
}
|
||||
}
|
||||
|
||||
.avatar-box {
|
||||
::v-deep .u-avatar {
|
||||
width: 2rem !important;
|
||||
height: 2rem !important;
|
||||
}
|
||||
}
|
||||
|
||||
.approver-list {
|
||||
border-radius: 8rpx;
|
||||
height: 140rpx;
|
||||
background-color: #F5F5F5;
|
||||
|
||||
.approver-list-l {
|
||||
flex: 1;
|
||||
|
||||
.approver-list-l-box {
|
||||
width: 120rpx;
|
||||
align-items: center;
|
||||
position: relative;
|
||||
|
||||
.tag {
|
||||
position: absolute;
|
||||
top: 50rpx;
|
||||
}
|
||||
|
||||
.approver-user-name {
|
||||
color: #303133;
|
||||
width: 120rpx;
|
||||
text-align: center;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.approver-list-r {
|
||||
height: 4.375rem;
|
||||
|
||||
.approver-list-r-box {
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
margin-right: 10px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
border-radius: 12px;
|
||||
background-color: #fff;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.bottom-btn {
|
||||
background-color: #fff;
|
||||
width: 100%;
|
||||
height: 88rpx;
|
||||
padding: 0 20rpx;
|
||||
color: #7f7f7f;
|
||||
|
||||
.btn {
|
||||
background-color: #f2f2f2;
|
||||
border-radius: 8rpx;
|
||||
height: 60rpx;
|
||||
padding-left: 10rpx;
|
||||
width: 100%;
|
||||
line-height: 60rpx;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
::v-deep .u-time-axis-item {
|
||||
margin-bottom: 0 !important;
|
||||
}
|
||||
|
||||
.timeLine {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
padding: 20rpx;
|
||||
|
||||
.u-time-axis-item {
|
||||
.u-time-axis-node {
|
||||
top: 0 !important;
|
||||
}
|
||||
}
|
||||
|
||||
.timeLine-right {
|
||||
padding-left: 0;
|
||||
padding-right: 40rpx !important;
|
||||
|
||||
&::before {
|
||||
left: 670rpx !important;
|
||||
}
|
||||
|
||||
.u-time-axis-item {
|
||||
.u-time-axis-node {
|
||||
left: 670rpx !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.timeLine-dot {
|
||||
width: 28rpx;
|
||||
height: 28rpx;
|
||||
border: 4rpx solid #FFFFFF;
|
||||
box-shadow: 0 6rpx 12rpx rgba(2, 7, 28, 0.16);
|
||||
border-radius: 50%;
|
||||
}
|
||||
|
||||
.timeLine-content {
|
||||
padding: 0 10rpx;
|
||||
|
||||
.timeLine-title {
|
||||
font-size: 30rpx;
|
||||
line-height: 36rpx;
|
||||
|
||||
.name {
|
||||
margin-bottom: 6rpx;
|
||||
}
|
||||
}
|
||||
|
||||
.timeLine-title2 {
|
||||
margin-top: 6rpx;
|
||||
background: rgba(255, 255, 255, 0.39);
|
||||
box-shadow: 0px 3px 10px rgba(0, 0, 0, 0.1);
|
||||
padding: 10rpx 20rpx;
|
||||
border-radius: 4px;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
.timeLine-desc {
|
||||
margin-top: 10rpx;
|
||||
font-size: 26rpx;
|
||||
line-height: 36rpx;
|
||||
color: #909399;
|
||||
margin-bottom: 10rpx;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user