初始提交

This commit is contained in:
2026-01-04 11:09:06 +08:00
commit 8fa31df250
1326 changed files with 213907 additions and 0 deletions

View 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>

View 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>

View 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>

View 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>

View 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>

View 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>

View 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>

View 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>

View 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>

View 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>

View 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>